library(tidyr)
library(ggplot2)
library(dplyr)
Attaching package: ‘dplyr’
The following objects are masked from ‘package:stats’:
filter, lag
The following objects are masked from ‘package:base’:
intersect, setdiff, setequal, union
library(matrixStats)
Attaching package: ‘matrixStats’
The following object is masked from ‘package:dplyr’:
count
library(DESeq2)
Loading required package: S4Vectors
Loading required package: stats4
Loading required package: BiocGenerics
Loading required package: parallel
Attaching package: ‘BiocGenerics’
The following objects are masked from ‘package:parallel’:
clusterApply, clusterApplyLB, clusterCall, clusterEvalQ, clusterExport, clusterMap, parApply,
parCapply, parLapply, parLapplyLB, parRapply, parSapply, parSapplyLB
The following objects are masked from ‘package:dplyr’:
combine, intersect, setdiff, union
The following objects are masked from ‘package:stats’:
IQR, mad, sd, var, xtabs
The following objects are masked from ‘package:base’:
anyDuplicated, append, as.data.frame, basename, cbind, colnames, dirname, do.call, duplicated, eval,
evalq, Filter, Find, get, grep, grepl, intersect, is.unsorted, lapply, Map, mapply, match, mget,
order, paste, pmax, pmax.int, pmin, pmin.int, Position, rank, rbind, Reduce, rownames, sapply,
setdiff, sort, table, tapply, union, unique, unsplit, which, which.max, which.min
Attaching package: ‘S4Vectors’
The following objects are masked from ‘package:dplyr’:
first, rename
The following object is masked from ‘package:tidyr’:
expand
The following object is masked from ‘package:base’:
expand.grid
Loading required package: IRanges
Attaching package: ‘IRanges’
The following objects are masked from ‘package:dplyr’:
collapse, desc, slice
Loading required package: GenomicRanges
Loading required package: GenomeInfoDb
Loading required package: SummarizedExperiment
Loading required package: Biobase
Welcome to Bioconductor
Vignettes contain introductory material; view with 'browseVignettes()'. To cite Bioconductor, see
'citation("Biobase")', and for packages 'citation("pkgname")'.
Attaching package: ‘Biobase’
The following objects are masked from ‘package:matrixStats’:
anyMissing, rowMedians
Loading required package: DelayedArray
Loading required package: BiocParallel
Attaching package: ‘DelayedArray’
The following objects are masked from ‘package:matrixStats’:
colMaxs, colMins, colRanges, rowMaxs, rowMins, rowRanges
The following objects are masked from ‘package:base’:
aperm, apply, rowsum
library(stringr)
library(ggExtra)
library(gridExtra)
Attaching package: ‘gridExtra’
The following object is masked from ‘package:Biobase’:
combine
The following object is masked from ‘package:BiocGenerics’:
combine
The following object is masked from ‘package:dplyr’:
combine
library(pheatmap)
library(reshape2)
Attaching package: ‘reshape2’
The following object is masked from ‘package:tidyr’:
smiths
library("ggalluvial")
give.n <- function(x){
return(c(y = mean(x), label = length(x)))
}
SNP-ASE output See https://github.com/orionzhou/rnaseq/blob/master/output.md
rds <- readRDS("ase_1Oct19.rds")
ase.reads <- rds
cpm <- readRDS("cpm.rds")
lib.size.tab <- data.frame(cpm$tl)
ase.reads$ratio <- ase.reads$allele1/(ase.reads$allele1 + ase.reads$allele2)
ase.reads$total.reads <- (ase.reads$allele1 + ase.reads$allele2)
ase.reads$contrast <- substr(ase.reads$sid,0,2)
ase.reads2 <- data.frame(ase.reads %>%
group_by(gid,contrast) %>%
dplyr::summarize(
mean.ratio = mean(ratio),
mean.count = mean(total.reads)))
Gene key B73 reference gene model cross-reference relative to v4 Downloaded from MaizeGDB 2020/01/22, 11:32am Reformatted to remove header
gene.key <- read.table("gene_model_xref_v4c.txt",sep="\t",header=T,stringsAsFactors = F)
dup.gene <- subset(gene.key,duplicated(gene.key$v4_gene_model))
gene.key <- subset(gene.key,!gene.key$v4_gene_model %in% dup.gene$v4_gene_model)
gene.key.BW.syntelogs <- subset(gene.key,nchar(gene.key$W22.Zm00004b.1.) == 14)[,c(1:4,8,10,20,24)]
gene.key.bp.syntelogs <- subset(gene.key,nchar(gene.key$PH207.Zm00008a.1.) == 14)[,c(1:4,8,10,20,24)]
gene.key.BWP <- subset(gene.key,nchar(gene.key$W22.Zm00004b.1.) == 14 & nchar(gene.key$PH207.Zm00008a.1.) == 14)[,c(1:4,8,10,20,24)]
Read in RER table and calculate RPM
imp.all <- read.table("combined_counts_all_concatinated_genomes_21Oct19.txt",header=T,stringsAsFactors = F) # update path
imp.all$ID <- str_replace_all(imp.all$ID,".v1.1","")
imp.all.rpm <- imp.all
for(i in 2:19){
imp.all.rpm[,i] <- imp.all[,i]/lib.size.tab[lib.size.tab$SampleID == colnames(imp.all[i]),"libSize"] * 1e6
}
imp.all2 <- subset(imp.all,!imp.all$ID %in% c("no_feature","ambiguous","too_low_aQual","not_aligned","alignment_not_unique")) # remove non-feature and ambiguous reads
imp.all2$ID[grepl("gene",imp.all2$ID)] <- substr(imp.all2$ID[grepl("gene",imp.all2$ID)],6,19) # fix formatting
Summary stats about libraries
imp.all.numerator <- subset(imp.all,!imp.all$ID %in% c("too_low_aQual","not_aligned","alignment_not_unique")) # remove non-feature and ambiguous reads
imp.all.denominator <- subset(imp.all,!imp.all$ID %in% c("too_low_aQual","not_aligned")) # remove non-feature and ambiguous reads
summary.stats.libraries <- data.frame(cbind(colSums(imp.all.denominator[,2:19]),(colSums(imp.all.numerator[,2:19])/colSums(imp.all.denominator[,2:19]))*100))
names(summary.stats.libraries) <- c("Reads","Percent_Unique")
mean(summary.stats.libraries$Percent_Unique)
[1] 16.78902
imp.all.rpm2 <- subset(imp.all.rpm,!imp.all.rpm$ID %in% c("no_feature","ambiguous","too_low_aQual","not_aligned","alignment_not_unique")) # remove non-feature and ambiguous reads
rownames(imp.all.rpm2) <- imp.all.rpm2$ID
imp.all.rpm2$ID <- NULL
rownames(imp.all.rpm2)[grepl("gene",rownames(imp.all.rpm2))] <- substr(rownames(imp.all.rpm2)[grepl("gene",rownames(imp.all.rpm2))],6,19) # fix formatting
imp.all.rpm2$BW.mean <- rowMeans(imp.all.rpm2[,1:3]) #ignore this (I hard coded column numbers later)
imp.all.rpm2$WB.mean <- rowMeans(imp.all.rpm2[,4:6]) #ignore this (I hard coded column numbers later)
imp.all.rpm2$B_ratio <- imp.all.rpm2$BW.mean/(imp.all.rpm2$BW.mean + imp.all.rpm2$WB.mean) #ignore this (I hard coded column numbers later)
imp.all.rpm2$feature_type <- ifelse(nchar(rownames(imp.all.rpm2)) == 21, "TE","gene") # Label features as TEs or genes
imp.all.rpm2$genome <- ifelse(grepl("Zm00001d",rownames(imp.all.rpm2)),"B73",ifelse(grepl("Zm00004b",rownames(imp.all.rpm2)),"W22","PH207")) # Label which genome the feature annotation is from
table(imp.all.rpm2$genome)
B73 PH207 W22
370788 280880 354627
For each pair of genotypes, make the same columns and then do the math for the maternal preference ratio
imp.BvW.rpm <- imp.all.rpm2[,c(1:6,22:23)]
imp.BvW.rpm$contrast <- "BW"
imp.BvW.rpm$type <- ifelse(imp.BvW.rpm$genome == "B73","A","B")
imp.BvW.rpm$feature <- rownames(imp.BvW.rpm)
imp.BvP.rpm <- imp.all.rpm2[,c(7:12,22:23)]
imp.BvP.rpm$contrast <- "BP"
imp.BvP.rpm$type <- ifelse(imp.BvW.rpm$genome == "B73","A","B")
imp.BvP.rpm$feature <- rownames(imp.BvP.rpm)
imp.WvP.rpm <- imp.all.rpm2[,c(13:18,22:23)]
imp.WvP.rpm$contrast <- "WP"
imp.WvP.rpm$type <- ifelse(imp.BvW.rpm$genome == "W22","A","B")
imp.WvP.rpm$feature <- rownames(imp.WvP.rpm)
names(imp.BvW.rpm) <- names(imp.BvP.rpm) <- names(imp.WvP.rpm) <- c("AB1","AB2","AB3","BA1","BA2","BA3","feature_type","genome","contrast","type","feature")
imp.recips.rpm <- data.frame(rbind(imp.BvW.rpm,imp.BvP.rpm,imp.WvP.rpm))
imp.recips.rpm$A_mean <- rowMeans(imp.recips.rpm[,1:3])
imp.recips.rpm$B_mean <- rowMeans(imp.recips.rpm[,4:6])
imp.recips.rpm$maternal_preference <- ifelse(imp.recips.rpm$type == "A",imp.recips.rpm$A_mean/(imp.recips.rpm$A_mean + imp.recips.rpm$B_mean),imp.recips.rpm$B_mean/(imp.recips.rpm$A_mean + imp.recips.rpm$B_mean)) # maternal preferance calculation
Plot the maternal preferance distributipns for each contrast, genome, and feature type
imp.recips.rpm.filter <- subset(imp.recips.rpm,imp.recips.rpm$A_mean >= 1 | imp.recips.rpm$B_mean >= 1)
ggplot(imp.recips.rpm.filter,aes(x=genome,y=maternal_preference,fill=genome))+ geom_abline(slope=0, intercept=.33) + geom_abline(slope=0, intercept=.66) + geom_violin() + theme_classic() + theme(panel.grid.major = element_blank(),panel.grid.minor = element_blank()) + facet_wrap(feature_type~contrast,scales="free_x") + stat_summary(fun.data = give.n, geom = "text")

Look at per-element or gene expression across development
dev.reads <- read.table("element_combined_counts_DevREDO_11Oct19.txt",header=T,stringsAsFactors = F,sep="\t")
rownames(dev.reads) <- dev.reads$element
dev.reads$element <- NULL
dev.reads2 <- dev.reads[,grep("te.g",colnames(dev.reads),invert = T)]
names(dev.reads2) <- substr(names(dev.reads2),0,nchar(names(dev.reads2))-7)
dev.reads3 <- dev.reads2[,grep("DS",colnames(dev.reads2))]
dev.rpm <- dev.reads3
for(i in 1:ncol(dev.rpm)){
dev.rpm[,i] <- dev.reads3[,i]/sum(dev.reads3[,i]) * 1e6
}
all.samples <- data.frame(names(dev.rpm))
all.samples$colname <- paste(names(dev.rpm))
all.samples <- separate(all.samples,names.dev.rpm.,c("tissue","details","experiment","genotype","rep"),sep="_")
Expected 5 pieces. Additional pieces discarded in 5 rows [142, 185, 186, 187, 203].
Make filter for potential maternal contamination MEGs (TE plus gene)
keep.columns <- c("endosperm_14dap_DS_B_1","endosperm_14dap_DS_B_2","endosperm_14dap_DS_B_3","seed_pericarp.18dap_DS_B_1","seed_pericarp.18dap_DS_B_2","seed_pericarp.18dap_DS_B_3")
B73.endoperi.stepflug <- subset(dev.rpm, rowMaxs(as.matrix(dev.rpm[,keep.columns])) > 0)[,keep.columns]
B73.endoperi.stepflug$mean.endo <- rowMeans(B73.endoperi.stepflug[,1:3])
B73.endoperi.stepflug$mean.peri <- rowMeans(B73.endoperi.stepflug[,4:6])
peri.preferred <- subset(B73.endoperi.stepflug,B73.endoperi.stepflug$mean.peri > 2 * B73.endoperi.stepflug$mean.endo)
nrow(peri.preferred)
[1] 23355
table(nchar(rownames(peri.preferred)))
14 21
13562 9793
peri.preferred.W22 <- subset(gene.key,gene.key$v4_gene_model %in% rownames(peri.preferred))[,"W22.Zm00004b.1."]
peri.preferred.PH207 <- subset(gene.key,gene.key$v4_gene_model %in% rownames(peri.preferred))[,"PH207.Zm00008a.1."]
Use DEseq to call significance over a log2FC cutoff of 1 (2-fold, which is the expected ratio)
imp.all.de.bw <- imp.all2[,2:7]
rownames(imp.all.de.bw) <- imp.all2$ID
#imp.all.de.keep.bw <- subset(imp.all.de.bw,rowSums(imp.all.de.bw >0) >= 3)
imp.all.de.keep.bw <- subset(imp.all.de.bw,rowSums(imp.all.de.bw) >= 10)
#imp.all.de.keep.bw <- subset(imp.all.de.bw,rowMeans(imp.all.rpm2[,1:3]) >= 0.5 | rowMeans(imp.all.rpm2[,4:6]) >= 0.5)
sample.info.bw <- as.data.frame(cbind(paste(names(imp.all.de.keep.bw)),c("BW","BW","BW","WB","WB","WB")))
rownames(sample.info.bw) <- sample.info.bw[,1]
sample_list.bw <- sample.info.bw[,2]
names(sample.info.bw) <- c("sample.info.bw","sample_list")
dds.bw <- DESeqDataSetFromMatrix(countData = imp.all.de.keep.bw,colData = sample.info.bw, design = ~ sample_list)
dds.bw <- DESeq(dds.bw)
estimating size factors
estimating dispersions
gene-wise dispersion estimates
mean-dispersion relationship
final dispersion estimates
fitting model and testing
res.bw <- results(dds.bw, lfcThreshold=1, altHypothesis = "greaterAbs")
drawLines <- function() abline(h=c(-1,1),col="dodgerblue",lwd=2)
#par(mfrow=c(2,2),mar=c(2,2,1,1))
ylim <- c(-2.5,2.5)
plotMA(res.bw, ylim=ylim); drawLines()

Plot results with respect to RER
out.bw <- data.frame(res.bw)
plot.out.bw <- merge(imp.recips.rpm,out.bw,by.x="feature",by.y="row.names",all=F)
plot.out.bw$sig <- ifelse(plot.out.bw$padj < 0.05, "TRUE", "FALSE")
plot.out2.bw <- subset(plot.out.bw,!is.na(plot.out.bw$sig) & plot.out.bw$contrast %in% c("BW","WB"))
plot.out2.bw$category <- ifelse(plot.out2.bw$padj < 0.05 & plot.out2.bw$log2FoldChange < -1 & (plot.out2.bw$maternal_preference > .9 | plot.out2.bw$maternal_preference < .1), "up",ifelse(plot.out2.bw$padj < 0.05 & plot.out2.bw$log2FoldChange > 1 & (plot.out2.bw$maternal_preference > .9 | plot.out2.bw$maternal_preference < .1),"down","notDE"))
plot.out2.bw$imprint <- ifelse(plot.out2.bw$category =="up" & plot.out2.bw$genome == "B73","MEG",
ifelse(plot.out2.bw$category =="down" & plot.out2.bw$genome == "B73","PEG",
ifelse(plot.out2.bw$category =="down" & plot.out2.bw$genome == "W22","MEG",
ifelse(plot.out2.bw$category =="up" & plot.out2.bw$genome == "W22","PEG","no.imprint"))))
ggplot(plot.out2.bw,aes(x=imprint,y=maternal_preference,fill=imprint)) + geom_violin() + theme_classic() + theme(panel.grid.major = element_blank(),panel.grid.minor = element_blank()) + facet_grid(genome~feature_type) + geom_abline(slope=0, intercept=.33) + geom_abline(slope=0, intercept=.66) + stat_summary(fun.data = give.n, geom = "text")

plot.out2.bw$filter.imprint <- ifelse(plot.out2.bw$imprint == "MEG" & (plot.out2.bw$feature %in% rownames(peri.preferred) | plot.out2.bw$feature %in% peri.preferred.W22),"filtered.MEG",plot.out2.bw$imprint)
Repeat for BP
imp.all.de.bp <- imp.all2[,8:13]
rownames(imp.all.de.bp) <- imp.all2$ID
#imp.all.de.keep.bp <- subset(imp.all.de.bp,rowSums(imp.all.de.bp >0) >= 3)
imp.all.de.keep.bp <- subset(imp.all.de.bp,rowSums(imp.all.de.bp) >= 10)
#imp.all.de.keep.bp <- subset(imp.all.de.bp,rowMeans(imp.all.rpm2[,7:9]) >= 0.5 | rowMeans(imp.all.rpm2[,10:12]) >= 0.5)
sample.info.bp <- as.data.frame(cbind(paste(names(imp.all.de.keep.bp)),c("BP","BP","BP","PB","PB","PB")))
rownames(sample.info.bp) <- sample.info.bp[,1]
sample_list.bp <- sample.info.bp[,2]
names(sample.info.bp) <- c("sample.info.bp","sample_list")
dds.bp <- DESeqDataSetFromMatrix(countData = imp.all.de.keep.bp,colData = sample.info.bp, design = ~ sample_list)
dds.bp <- DESeq(dds.bp)
estimating size factors
estimating dispersions
gene-wise dispersion estimates
mean-dispersion relationship
final dispersion estimates
fitting model and testing
res.bp <- results(dds.bp, lfcThreshold=1, altHypothesis = "greaterAbs")
#par(mfrow=c(2,2),mar=c(2,2,1,1))
ylim <- c(-2.5,2.5)
plotMA(res.bp, ylim=ylim); drawLines()

Plot results with respect to RER
out.bp <- data.frame(res.bp)
plot.out.bp <- merge(imp.recips.rpm,out.bp,by.x="feature",by.y="row.names",all=F)
plot.out.bp$sig <- ifelse(plot.out.bp$padj < 0.05, "TRUE", "FALSE")
plot.out2.bp <- subset(plot.out.bp,!is.na(plot.out.bp$sig) & plot.out.bp$contrast %in% c("BP","PB"))
plot.out2.bp$category <- ifelse(plot.out2.bp$padj < 0.05 & plot.out2.bp$log2FoldChange < -1 & (plot.out2.bp$maternal_preference > .9 | plot.out2.bp$maternal_preference < .1), "up",ifelse(plot.out2.bp$padj < 0.05 & plot.out2.bp$log2FoldChange > 1 & (plot.out2.bp$maternal_preference > .9 | plot.out2.bp$maternal_preference < .1),"down","notDE"))
plot.out2.bp$imprint <- ifelse(plot.out2.bp$category =="up" & plot.out2.bp$genome == "B73","MEG",
ifelse(plot.out2.bp$category =="down" & plot.out2.bp$genome == "B73","PEG",
ifelse(plot.out2.bp$category =="down" & plot.out2.bp$genome == "PH207","MEG",
ifelse(plot.out2.bp$category =="up" & plot.out2.bp$genome == "PH207","PEG","no.imprint"))))
ggplot(plot.out2.bp,aes(x=imprint,y=maternal_preference,fill=imprint)) + geom_violin() + theme_classic() + theme(panel.grid.major = element_blank(),panel.grid.minor = element_blank()) + facet_grid(genome~feature_type) + geom_abline(slope=0, intercept=.33) + geom_abline(slope=0, intercept=.66) + stat_summary(fun.data = give.n, geom = "text")

plot.out2.bp$filter.imprint <- ifelse(plot.out2.bp$imprint == "MEG" & (plot.out2.bp$feature %in% rownames(peri.preferred) | plot.out2.bp$feature %in% peri.preferred.PH207),"filtered.MEG",plot.out2.bp$imprint)
Repeat for WP
imp.all.de.wp <- imp.all2[,14:19]
rownames(imp.all.de.wp) <- imp.all2$ID
#imp.all.de.keep.wp <- subset(imp.all.de.wp,rowSums(imp.all.de.wp >0) >= 3)
imp.all.de.keep.wp <- subset(imp.all.de.wp,rowSums(imp.all.de.wp) >= 10)
#imp.all.de.keep.wp <- subset(imp.all.de.wp,rowMeans(imp.all.rpm2[,13:15]) >= 0.5 | rowMeans(imp.all.rpm2[,16:18]) >= 0.5)
sample.info.wp <- as.data.frame(cbind(paste(names(imp.all.de.keep.wp)),c("WP","WP","WP","PW","PW","PW")))
rownames(sample.info.wp) <- sample.info.wp[,1]
sample_list.wp <- sample.info.wp[,2]
names(sample.info.wp) <- c("sample.info.wp","sample_list")
dds.wp <- DESeqDataSetFromMatrix(countData = imp.all.de.keep.wp,colData = sample.info.wp, design = ~ sample_list)
dds.wp <- DESeq(dds.wp)
estimating size factors
estimating dispersions
gene-wise dispersion estimates
mean-dispersion relationship
final dispersion estimates
fitting model and testing
res.wp <- results(dds.wp, lfcThreshold=1, altHypothesis = "greaterAbs")
drawLines <- function() abline(h=c(-1,1),col="dodgerblue",lwd=2)
#par(mfrow=c(2,2),mar=c(2,2,1,1))
ylim <- c(-2.5,2.5)
plotMA(res.wp, ylim=ylim); drawLines()

Plot results with respect to the RER
out.wp <- data.frame(res.wp)
plot.out.wp <- merge(imp.recips.rpm,out.wp,by.x="feature",by.y="row.names",all=F)
plot.out.wp$sig <- ifelse(plot.out.wp$padj < 0.05, "TRUE", "FALSE")
plot.out2.wp <- subset(plot.out.wp,!is.na(plot.out.wp$sig) & plot.out.wp$contrast %in% c("WP","PW"))
plot.out2.wp$category <- ifelse(plot.out2.wp$padj < 0.05 & plot.out2.wp$log2FoldChange < -1 & (plot.out2.wp$maternal_preference > .9 | plot.out2.wp$maternal_preference < .1), "up",ifelse(plot.out2.wp$padj < 0.05 & plot.out2.wp$log2FoldChange > 1 & (plot.out2.wp$maternal_preference > .9 | plot.out2.wp$maternal_preference < .1),"down","notDE"))
plot.out2.wp$imprint <- ifelse(plot.out2.wp$category =="up" & plot.out2.wp$genome == "PH207","MEG",
ifelse(plot.out2.wp$category =="down" & plot.out2.wp$genome == "PH207","PEG",
ifelse(plot.out2.wp$category =="down" & plot.out2.wp$genome == "W22","MEG",
ifelse(plot.out2.wp$category =="up" & plot.out2.wp$genome == "W22","PEG","no.imprint"))))
ggplot(plot.out2.wp,aes(x=imprint,y=maternal_preference,fill=imprint)) + geom_violin() + theme_classic() + theme(panel.grid.major = element_blank(),panel.grid.minor = element_blank()) + facet_grid(genome~feature_type) + geom_abline(slope=0, intercept=.33) + geom_abline(slope=0, intercept=.66) + stat_summary(fun.data = give.n, geom = "text")

plot.out2.wp$filter.imprint <- ifelse(plot.out2.wp$imprint == "MEG" & (plot.out2.wp$feature %in% peri.preferred.W22 | plot.out2.wp$feature %in% peri.preferred.PH207),"filtered.MEG",plot.out2.wp$imprint)
Summarize imprinting calls
summary.bw <- data.frame(plot.out2.bw %>% group_by(genome,feature_type,filter.imprint,contrast) %>% summarize(count = n()))
summary.bp <- data.frame(plot.out2.bp %>% group_by(genome,feature_type,filter.imprint,contrast) %>% summarize(count = n()))
summary.wp <- data.frame(plot.out2.wp %>% group_by(genome,feature_type,filter.imprint,contrast) %>% summarize(count = n()))
summary.combined <- rbind(summary.bw,summary.bp,summary.wp)
summary.combined
genome feature_type filter.imprint contrast count
1 B73 gene filtered.MEG BW 19
2 B73 gene MEG BW 150
3 B73 gene no.imprint BW 14882
4 B73 gene PEG BW 73
5 B73 TE MEG BW 107
6 B73 TE no.imprint BW 1819
7 B73 TE PEG BW 7
8 W22 gene filtered.MEG BW 4
9 W22 gene MEG BW 117
10 W22 gene no.imprint BW 14450
11 W22 gene PEG BW 69
12 W22 TE MEG BW 95
13 W22 TE no.imprint BW 1773
14 W22 TE PEG BW 3
15 B73 gene filtered.MEG BP 15
16 B73 gene MEG BP 123
17 B73 gene no.imprint BP 16253
18 B73 gene PEG BP 79
19 B73 TE MEG BP 102
20 B73 TE no.imprint BP 2315
21 B73 TE PEG BP 10
22 PH207 gene filtered.MEG BP 19
23 PH207 gene MEG BP 96
24 PH207 gene no.imprint BP 14611
25 PH207 gene PEG BP 77
26 PH207 TE MEG BP 94
27 PH207 TE no.imprint BP 1406
28 PH207 gene filtered.MEG WP 2
29 PH207 gene MEG WP 81
30 PH207 gene no.imprint WP 14236
31 PH207 gene PEG WP 64
32 PH207 TE MEG WP 82
33 PH207 TE no.imprint WP 1667
34 PH207 TE PEG WP 2
35 W22 gene filtered.MEG WP 3
36 W22 gene MEG WP 108
37 W22 gene no.imprint WP 15558
38 W22 gene PEG WP 56
39 W22 TE MEG WP 91
40 W22 TE no.imprint WP 2636
41 W22 TE PEG WP 5
summary.combined2 <- subset(summary.combined,summary.combined$filter.imprint %in% c("MEG","PEG"))
summary.combined2[summary.combined2$filter.imprint == "PEG","count"] <- -summary.combined2[summary.combined2$filter.imprint == "PEG","count"]
summary.combined2$label <- paste(summary.combined2$genome,summary.combined2$contrast,sep="_")
ggplot() + geom_bar(aes(x=label,y=count,fill=filter.imprint),data=summary.combined2,stat="identity",position = "identity") + theme_classic() + theme(axis.text.x = element_text(angle=90, hjust=1)) + facet_grid(.~feature_type,scales="free") + scale_fill_manual(values=c("magenta2","navy"))

Average imprinted genes
imp.genes <- subset(summary.combined2,summary.combined2$feature_type == "gene")
imp.genes[imp.genes$filter.imprint == "PEG","count"] <- -imp.genes[imp.genes$filter.imprint == "PEG","count"]
imp.genes2 <- imp.genes%>% group_by(label) %>% dplyr::summarise(total.imprint = sum(count))
mean(imp.genes2$total.imprint)
[1] 182.1667
mean(subset(imp.genes,imp.genes$filter.imprint == "MEG")[,"count"])
[1] 112.5
mean(subset(imp.genes,imp.genes$filter.imprint == "PEG")[,"count"])
[1] 69.66667
mean(subset(summary.combined2,summary.combined2$feature_type == "TE" & summary.combined2$filter.imprint == "MEG")[,"count"])
[1] 95.16667
Use to make venns - B73 vs W22
compare.bw.syntelogs <- subset(plot.out2.bw,plot.out2.bw$feature_type == "gene")
compare.bw.syntelogs$syntelog <- ifelse(compare.bw.syntelogs$feature %in% gene.key.BW.syntelogs$v4_gene_model | compare.bw.syntelogs$feature %in% gene.key.BW.syntelogs$W22.Zm00004b.1., "syntelog","non.syntelog")
table(compare.bw.syntelogs[,c("genome","filter.imprint","syntelog")])
, , syntelog = non.syntelog
filter.imprint
genome filtered.MEG MEG no.imprint PEG
B73 2 107 2893 13
W22 0 89 2733 11
, , syntelog = syntelog
filter.imprint
genome filtered.MEG MEG no.imprint PEG
B73 17 43 11989 60
W22 4 28 11717 58
compare.bw.syntelogs.b73 <- merge(subset(compare.bw.syntelogs,compare.bw.syntelogs$syntelog == "syntelog" & compare.bw.syntelogs$genome == "B73"),gene.key.BW.syntelogs,by.x="feature",by.y="v4_gene_model",all.x=T)
compare.bw.syntelogs.w22 <- subset(compare.bw.syntelogs,compare.bw.syntelogs$syntelog == "syntelog" & compare.bw.syntelogs$genome == "W22")
compare.bw.syntelogs.merge <- merge(compare.bw.syntelogs.b73,compare.bw.syntelogs.w22,by.x = "W22.Zm00004b.1.",by.y="feature",all=T)
names(compare.bw.syntelogs.merge) <- gsub(".x",".B73",names(compare.bw.syntelogs.merge),fixed = T)
names(compare.bw.syntelogs.merge) <- gsub(".y",".W22",names(compare.bw.syntelogs.merge),fixed = T)
compare.bw.syntelogs.merge$imprint.B73[is.na(compare.bw.syntelogs.merge$imprint.B73)] <- "no.imprint"
compare.bw.syntelogs.merge$imprint.W22[is.na(compare.bw.syntelogs.merge$imprint.W22)] <- "no.imprint"
compare.bw.syntelogs.merge$filter.imprint.B73[is.na(compare.bw.syntelogs.merge$filter.imprint.B73)] <- "no.imprint"
compare.bw.syntelogs.merge$filter.imprint.W22[is.na(compare.bw.syntelogs.merge$filter.imprint.W22)] <- "no.imprint"
table(compare.bw.syntelogs.merge[,c("filter.imprint.B73","filter.imprint.W22")])
filter.imprint.W22
filter.imprint.B73 filtered.MEG MEG no.imprint PEG
filtered.MEG 0 0 17 0
MEG 0 17 26 0
no.imprint 4 11 13088 19
PEG 0 0 21 39
Use to make venns - B73 vs PH207
compare.bp.syntelogs <- subset(plot.out2.bp,plot.out2.bp$feature_type == "gene")
compare.bp.syntelogs$syntelog <- ifelse(compare.bp.syntelogs$feature %in% gene.key.bp.syntelogs$v4_gene_model | compare.bp.syntelogs$feature %in% gene.key.bp.syntelogs$PH207.Zm00008a.1., "syntelog","non.syntelog")
table(compare.bp.syntelogs[,c("genome","filter.imprint","syntelog")])
, , syntelog = non.syntelog
filter.imprint
genome filtered.MEG MEG no.imprint PEG
B73 2 65 1737 9
PH207 0 44 2161 5
, , syntelog = syntelog
filter.imprint
genome filtered.MEG MEG no.imprint PEG
B73 13 58 14516 70
PH207 19 52 12450 72
compare.bp.syntelogs.b73 <- merge(subset(compare.bp.syntelogs,compare.bp.syntelogs$syntelog == "syntelog" & compare.bp.syntelogs$genome == "B73"),gene.key.bp.syntelogs,by.x="feature",by.y="v4_gene_model",all.x=T)
compare.bp.syntelogs.PH207 <- subset(compare.bp.syntelogs,compare.bp.syntelogs$syntelog == "syntelog" & compare.bp.syntelogs$genome == "PH207")
compare.bp.syntelogs.merge <- merge(compare.bp.syntelogs.b73,compare.bp.syntelogs.PH207,by.x = "PH207.Zm00008a.1.",by.y="feature",all=T)
names(compare.bp.syntelogs.merge) <- gsub(".x",".B73",names(compare.bp.syntelogs.merge),fixed = T)
names(compare.bp.syntelogs.merge) <- gsub(".y",".PH207",names(compare.bp.syntelogs.merge),fixed = T)
compare.bp.syntelogs.merge$imprint.B73[is.na(compare.bp.syntelogs.merge$imprint.B73)] <- "no.imprint"
compare.bp.syntelogs.merge$imprint.PH207[is.na(compare.bp.syntelogs.merge$imprint.PH207)] <- "no.imprint"
compare.bp.syntelogs.merge$filter.imprint.B73[is.na(compare.bp.syntelogs.merge$filter.imprint.B73)] <- "no.imprint"
compare.bp.syntelogs.merge$filter.imprint.PH207[is.na(compare.bp.syntelogs.merge$filter.imprint.PH207)] <- "no.imprint"
table(compare.bp.syntelogs.merge[,c("filter.imprint.B73","filter.imprint.PH207")])
filter.imprint.PH207
filter.imprint.B73 filtered.MEG MEG no.imprint PEG
filtered.MEG 1 0 11 1
MEG 0 19 38 1
no.imprint 18 31 15496 30
PEG 0 2 26 42
matTE venns
te.anno.compare <- read.table("non-redundant_TEs_by_annptation_4genomes_24Jan20.txt",header = T,stringsAsFactors = F)
B73 vs W22 TEs
shared.BW <- subset(te.anno.compare, te.anno.compare$B73 == "present" & nchar(te.anno.compare$B73.annotation == 21) & te.anno.compare$W22 == "present" & nchar(te.anno.compare$W22.annotation == 21))
compare.bw.TE <- subset(plot.out2.bw,plot.out2.bw$feature_type == "TE" & plot.out2.bw$filter.imprint == "MEG")
compare.bw.TE$shared <- ifelse(compare.bw.TE$feature %in% shared.BW$B73.annotation | compare.bw.TE$feature %in% shared.BW$W22.annotation, "shared","variable")
table(compare.bw.TE[,c("genome","filter.imprint","shared")])
, , shared = shared
filter.imprint
genome MEG
B73 9
W22 17
, , shared = variable
filter.imprint
genome MEG
B73 98
W22 78
compare.bw.TE.b <- merge(subset(compare.bw.TE,compare.bw.TE$shared == "shared" & compare.bw.TE$genome == "B73"),shared.BW[,c("B73.annotation","W22.annotation")],by.x="feature",by.y="B73.annotation",all.x=T)
compare.bw.TE.w <- subset(compare.bw.TE,compare.bw.TE$shared == "shared" & compare.bw.TE$genome == "W22")
compare.bw.TE.merge <- merge(compare.bw.TE.b, compare.bw.TE.w, by.x="W22.annotation",by.y="feature",all=T)
names(compare.bw.TE.merge) <- gsub(".x",".B73",names(compare.bw.TE.merge),fixed = T)
names(compare.bw.TE.merge) <- gsub(".y",".W22",names(compare.bw.TE.merge),fixed = T)
compare.bw.TE.merge$filter.imprint.B73[is.na(compare.bw.TE.merge$filter.imprint.B73)] <- "no.imprint"
compare.bw.TE.merge$filter.imprint.W22[is.na(compare.bw.TE.merge$filter.imprint.W22)] <- "no.imprint"
table(compare.bw.TE.merge[,c("filter.imprint.B73","filter.imprint.W22")])
filter.imprint.W22
filter.imprint.B73 MEG no.imprint
MEG 4 5
no.imprint 13 0
B73 vs PH207 TEs
shared.bp <- subset(te.anno.compare, te.anno.compare$B73 == "present" & nchar(te.anno.compare$B73.annotation) == 21 & te.anno.compare$PH207 == "present" & nchar(te.anno.compare$PH207.annotation) == 21)
compare.bp.TE <- subset(plot.out2.bp,plot.out2.bp$feature_type == "TE" & plot.out2.bp$filter.imprint == "MEG")
compare.bp.TE$shared <- ifelse(compare.bp.TE$feature %in% shared.bp$B73.annotation | compare.bp.TE$feature %in% shared.bp$P207.annotation, "shared","variable")
table(compare.bp.TE[,c("genome","filter.imprint","shared")])
, , shared = shared
filter.imprint
genome MEG
B73 11
PH207 0
, , shared = variable
filter.imprint
genome MEG
B73 91
PH207 94
compare.bp.TE.b <- merge(subset(compare.bp.TE,compare.bp.TE$shared == "shared" & compare.bp.TE$genome == "B73"),shared.bp[,c("B73.annotation","PH207.annotation")],by.x="feature",by.y="B73.annotation",all.x=T)
compare.bp.TE.w <- subset(compare.bp.TE,compare.bp.TE$shared == "shared" & compare.bp.TE$genome == "PH207")
compare.bp.TE.merge <- merge(compare.bp.TE.b, compare.bp.TE.w, by.x="PH207.annotation",by.y="feature",all=T)
names(compare.bp.TE.merge) <- gsub(".x",".B73",names(compare.bp.TE.merge),fixed = T)
names(compare.bp.TE.merge) <- gsub(".y",".PH207",names(compare.bp.TE.merge),fixed = T)
compare.bp.TE.merge$filter.imprint.B73[is.na(compare.bp.TE.merge$filter.imprint.B73)] <- "no.imprint"
compare.bp.TE.merge$filter.imprint.PH207[is.na(compare.bp.TE.merge$filter.imprint.PH207)] <- "no.imprint"
table(compare.bp.TE.merge[,c("filter.imprint.B73","filter.imprint.PH207")])
filter.imprint.PH207
filter.imprint.B73 no.imprint
MEG 11
How many MEGs called in only one direction were due to cutoffs and no data? Use B73 by W22 contrast for numbers
B73.only.MEG.bw <- subset(compare.bw.syntelogs.merge,compare.bw.syntelogs.merge$filter.imprint.B73 == "MEG" & compare.bw.syntelogs.merge$filter.imprint.W22 == "no.imprint")
B73.only.MEG.bw$category <- ifelse(is.na(B73.only.MEG.bw$maternal_preference.W22),"low.coverage",
ifelse(B73.only.MEG.bw$maternal_preference.W22 > 0.75,"RER>0.75","no.imprint"))
B73.only.MEG.bw$group <- "B73.MEG.BW"
W22.only.MEG.bw <- subset(compare.bw.syntelogs.merge,compare.bw.syntelogs.merge$filter.imprint.B73 == "no.imprint" & compare.bw.syntelogs.merge$filter.imprint.W22 == "MEG")
W22.only.MEG.bw$category <- ifelse(is.na(W22.only.MEG.bw$maternal_preference.B73),"low.coverage",
ifelse(W22.only.MEG.bw$maternal_preference.B73 > 0.75,"RER>0.75","no.imprint"))
W22.only.MEG.bw$group <- "W22.MEG.BW"
B73.only.MEG.bp <- subset(compare.bp.syntelogs.merge,compare.bp.syntelogs.merge$filter.imprint.B73 == "MEG" & compare.bp.syntelogs.merge$filter.imprint.PH207 == "no.imprint")
B73.only.MEG.bp$category <- ifelse(is.na(B73.only.MEG.bp$maternal_preference.PH207),"low.coverage",
ifelse(B73.only.MEG.bp$maternal_preference.PH207 > 0.75,"RER>0.75","no.imprint"))
B73.only.MEG.bp$group <- "B73.MEG.BP"
PH207.only.MEG.bp <- subset(compare.bp.syntelogs.merge,compare.bp.syntelogs.merge$filter.imprint.B73 == "no.imprint" & compare.bp.syntelogs.merge$filter.imprint.PH207 == "MEG")
PH207.only.MEG.bp$category <- ifelse(is.na(PH207.only.MEG.bp$maternal_preference.B73),"low.coverage",
ifelse(PH207.only.MEG.bp$maternal_preference.B73 > 0.75,"RER>0.75","no.imprint"))
PH207.only.MEG.bp$group <- "PH207.MEG.BP"
not.enough.mat.bias <- sum(nrow(subset(B73.only.MEG.bw,B73.only.MEG.bw$maternal_preference.W22 > 0.75)),
nrow(subset(W22.only.MEG.bw,W22.only.MEG.bw$maternal_preference.B73 > 0.75)))
total.non.overlap <- sum(nrow(B73.only.MEG.bw),nrow(W22.only.MEG.bw))
not.enough.mat.bias / total.non.overlap * 100
[1] 27.02703
not.enough.mat.data <- sum(nrow(subset(B73.only.MEG.bw,is.na(B73.only.MEG.bw$maternal_preference.W22))),
nrow(subset(W22.only.MEG.bw,is.na(W22.only.MEG.bw$maternal_preference.B73))))
not.enough.mat.data / total.non.overlap * 100
[1] 32.43243
MEG.non.overlap <- rbind(B73.only.MEG.bw[,c("group","category")],W22.only.MEG.bw[,c("group","category")],B73.only.MEG.bp[,c("group","category")],PH207.only.MEG.bp[,c("group","category")])
plot.MEG.non.overlap <- data.frame(table(MEG.non.overlap))
plot.MEG.non.overlap$category <- factor(plot.MEG.non.overlap$category, levels=c("no.imprint","low.coverage","RER>0.75"))
plot.MEG.non.overlap$group <- factor(plot.MEG.non.overlap$group,levels=c("B73.MEG.BW","W22.MEG.BW","B73.MEG.BP","PH207.MEG.BP"))
ggplot() + geom_bar(aes(x=group,y=Freq,fill=category),data=plot.MEG.non.overlap,stat="identity",position=position_fill()) + theme_classic() + theme(axis.text.x = element_text(angle=90, hjust=1)) + scale_fill_brewer(palette="Reds")

What about PEGs? Use B73 by W22 contrast for numbers
B73.only.PEG.bw <- subset(compare.bw.syntelogs.merge,compare.bw.syntelogs.merge$filter.imprint.B73 == "PEG" & compare.bw.syntelogs.merge$filter.imprint.W22 == "no.imprint")
B73.only.PEG.bw$category <- ifelse(is.na(B73.only.PEG.bw$maternal_preference.W22),"low.coverage",
ifelse(B73.only.PEG.bw$maternal_preference.W22 < 0.25,"RER<0.25","no.imprint"))
B73.only.PEG.bw$group <- "B73.PEG.BW"
W22.only.PEG.bw <- subset(compare.bw.syntelogs.merge,compare.bw.syntelogs.merge$filter.imprint.B73 == "no.imprint" & compare.bw.syntelogs.merge$filter.imprint.W22 == "PEG")
W22.only.PEG.bw$category <- ifelse(is.na(W22.only.PEG.bw$maternal_preference.B73),"low.coverage",
ifelse(W22.only.PEG.bw$maternal_preference.B73 < 0.25,"RER<0.25","no.imprint"))
W22.only.PEG.bw$group <- "W22.PEG.BW"
B73.only.PEG.bp <- subset(compare.bp.syntelogs.merge,compare.bp.syntelogs.merge$filter.imprint.B73 == "PEG" & compare.bp.syntelogs.merge$filter.imprint.PH207 == "no.imprint")
B73.only.PEG.bp$category <- ifelse(is.na(B73.only.PEG.bp$maternal_preference.PH207),"low.coverage",
ifelse(B73.only.PEG.bp$maternal_preference.PH207 < 0.25,"RER<0.25","no.imprint"))
B73.only.PEG.bp$group <- "B73.PEG.BP"
PH207.only.PEG.bp <- subset(compare.bp.syntelogs.merge,compare.bp.syntelogs.merge$filter.imprint.B73 == "no.imprint" & compare.bp.syntelogs.merge$filter.imprint.PH207 == "PEG")
PH207.only.PEG.bp$category <- ifelse(is.na(PH207.only.PEG.bp$maternal_preference.B73),"low.coverage",
ifelse(PH207.only.PEG.bp$maternal_preference.B73 < 0.25,"RER<0.25","no.imprint"))
PH207.only.PEG.bp$group <- "PH207.PEG.BP"
not.enough.pat.bias <- sum(nrow(subset(B73.only.PEG.bw,B73.only.PEG.bw$maternal_preference.W22 < 0.25)),
nrow(subset(W22.only.PEG.bw,W22.only.PEG.bw$maternal_preference.B73 > 0.25)))
total.non.overlap.pat <- sum(nrow(B73.only.PEG.bw),nrow(W22.only.PEG.bw))
not.enough.pat.bias / total.non.overlap.pat * 100
[1] 57.5
not.enough.pat.data <- sum(nrow(subset(B73.only.PEG.bw,is.na(B73.only.PEG.bw$maternal_preference.W22))),
nrow(subset(W22.only.PEG.bw,is.na(W22.only.PEG.bw$maternal_preference.B73))))
not.enough.pat.data / total.non.overlap.pat * 100
[1] 7.5
PEG.non.overlap <- rbind(B73.only.PEG.bw[,c("group","category")],W22.only.PEG.bw[,c("group","category")],B73.only.PEG.bp[,c("group","category")],PH207.only.PEG.bp[,c("group","category")])
plot.PEG.non.overlap <- data.frame(table(PEG.non.overlap))
plot.PEG.non.overlap$category <- factor(plot.PEG.non.overlap$category, levels=c("no.imprint","low.coverage","RER<0.25"))
plot.PEG.non.overlap$group <- factor(plot.PEG.non.overlap$group,levels=c("B73.PEG.BW","W22.PEG.BW","B73.PEG.BP","PH207.PEG.BP"))
ggplot() + geom_bar(aes(x=group,y=Freq,fill=category),data=plot.PEG.non.overlap,stat="identity",position=position_fill()) + theme_classic() + theme(axis.text.x = element_text(angle=90, hjust=1)) + scale_fill_brewer(palette="Blues")

Combined
a <- ggplot() + geom_bar(aes(x=group,y=Freq,fill=category),data=plot.MEG.non.overlap,stat="identity",position=position_fill()) + theme_classic() + theme(axis.text.x = element_text(angle=90, hjust=1)) + scale_fill_brewer(palette="Reds")
b <- ggplot() + geom_bar(aes(x=group,y=Freq,fill=category),data=plot.PEG.non.overlap,stat="identity",position=position_fill()) + theme_classic() + theme(axis.text.x = element_text(angle=90, hjust=1)) + scale_fill_brewer(palette="Blues")
grid.arrange(a,b,nrow=1)

Create file of BW calls
tmp.meg <- subset(compare.bw.syntelogs.merge,compare.bw.syntelogs.merge$filter.imprint.B73 == "MEG" & compare.bw.syntelogs.merge$filter.imprint.W22 == "MEG")
tmp.peg <- subset(compare.bw.syntelogs.merge,compare.bw.syntelogs.merge$filter.imprint.B73 == "PEG" & compare.bw.syntelogs.merge$filter.imprint.W22 == "PEG")
tmp3 <- subset(compare.bw.syntelogs.merge,compare.bw.syntelogs.merge$filter.imprint.B73 != compare.bw.syntelogs.merge$filter.imprint.W22)
bw.out.share <- plot.out2.bw
bw.out.share$syntelog <- ifelse(bw.out.share$feature_type == "TE", "na.te",
ifelse(bw.out.share$feature %in% gene.key.BW.syntelogs$v4_gene_model
| bw.out.share$feature %in% gene.key.BW.syntelogs$W22.Zm00004b.1., "syntelog","non.syntelog"))
bw.out.share$imprinted.both <- ifelse(bw.out.share$syntelog == "na.te" | bw.out.share$syntelog == "non.syntelog",bw.out.share$syntelog,
ifelse(bw.out.share$feature %in% tmp.meg$feature | bw.out.share$feature %in% tmp.meg$W22.Zm00004b.1.,"both.megs",
ifelse(bw.out.share$feature %in% tmp.peg$feature | bw.out.share$feature %in% tmp.peg$W22.Zm00004b.1.,"both.pegs",
ifelse(bw.out.share$feature %in% tmp3$feature | bw.out.share$feature %in% tmp3$W22.Zm00004b.1.,"inconsistent.imprint","no.imprint"))))
tmp1 <- bw.out.share[bw.out.share$genome == "B73",c("feature","filter.imprint","syntelog","imprinted.both")]
names(tmp1)[2:4] <- c("filter.imprint.BW","syntelog.BW","imprinted.both.BW")
tmp2 <- plot.out2.bp[plot.out2.bp$genome == "B73",c("feature","filter.imprint")]
names(tmp2)[2] <- "filter.imprint.BP"
tmp2$syntelog.BP <- ifelse(nchar(tmp2$feature) == 21, "na.te",
ifelse(tmp2$feature %in% gene.key.bp.syntelogs$v4_gene_model, "syntelog","non.syntelog"))
b.both.contrasts <- merge(tmp1,tmp2,by="feature",all=T)
b.both.contrasts$filter.imprint.BW[is.na(b.both.contrasts$filter.imprint.BW)] <- "not.detected"
b.both.contrasts$filter.imprint.BP[is.na(b.both.contrasts$filter.imprint.BP)] <- "not.detected"
b.both.contrasts[is.na(b.both.contrasts)] <- "NA"
b.both.contrasts$feature_type <- ifelse(nchar(b.both.contrasts$feature) == 21, "TE","gene")
b.both.contrasts$imprint <- ifelse(b.both.contrasts$feature_type == "TE" & (b.both.contrasts$filter.imprint.BP == "MEG" | b.both.contrasts$filter.imprint.BW == "MEG"),"matTE",
ifelse(b.both.contrasts$filter.imprint.BP == "MEG" | b.both.contrasts$filter.imprint.BW == "MEG","MEG",
ifelse(b.both.contrasts$feature_type == "gene" & (b.both.contrasts$filter.imprint.BP == "PEG" | b.both.contrasts$filter.imprint.BW == "PEG"),"PEG","no.imprint")))
b.both.contrasts$syntelog <- ifelse(b.both.contrasts$feature == "TE", "na.TE",
ifelse(b.both.contrasts$feature %in% gene.key.BWP$v4_gene_model, "conserved.maize","variable"))
table(b.both.contrasts[,c("filter.imprint.BW","filter.imprint.BP")])
filter.imprint.BP
filter.imprint.BW filtered.MEG MEG no.imprint not.detected PEG
filtered.MEG 3 0 14 2 0
MEG 0 135 75 47 0
no.imprint 9 54 14819 1792 27
not.detected 3 36 3632 0 20
PEG 0 0 28 10 42
What proportion of the syntelog vs non-syntelogs agree across contrasts?
b.both.contrasts$agreement <- ifelse(b.both.contrasts$filter.imprint.BP == b.both.contrasts$filter.imprint.BW, "matched.call","mismatched_call")
syntelog.agreement <- data.frame(b.both.contrasts %>% group_by(syntelog,agreement,imprint) %>% dplyr::summarise(features = n()))
ggplot() + geom_bar(aes(x=syntelog,y=features,fill=agreement),data=subset(syntelog.agreement,syntelog.agreement$imprint != "no.imprint"),stat="identity",position = position_fill()) + theme_classic() + theme(axis.text.x = element_text(angle=90, hjust=1)) + facet_grid(.~imprint,scales="free")

Compare to SNP-ASE method
tmp <- subset(ase.reads2,ase.reads2$contrast == "BW" & ase.reads2$mean.count >= 10)
names(tmp) <- c("gid","ase.contrast","ase.BW.ratio","ase.BW.count")
bw.merge <- merge(plot.out2.bw,tmp,by.x="feature",by.y="gid",all.x=T)
tmp2 <- subset(ase.reads2,ase.reads2$contrast == "WB" & ase.reads2$mean.count >= 10)
names(tmp2) <- c("gid","ase.contrast","ase.WB.ratio","ase.WB.count")
bw.merge2 <- merge(bw.merge,tmp2,by.x="feature",by.y="gid",all.x=T)
bw.merge2$genotype.biased <- ifelse(bw.merge2$ase.BW.ratio >= 0.9 & bw.merge2$ase.WB.ratio <= 0.1, "B73.biased",ifelse(bw.merge2$ase.WB.ratio >= 0.9 & bw.merge2$ase.BW.ratio <= 0.1,"W22.biased","fine"))
bw.merge2$group <- factor(ifelse(bw.merge2$genotype.biased == "fine",bw.merge2$filter.imprint,bw.merge2$genotype.biased),levels = c("B73.biased","W22.biased","MEG","PEG","no.imprint"))
a <- ggExtra::ggMarginal(ggplot(bw.merge2,aes(x=ase.BW.ratio,y=maternal_preference)) + theme_classic() + geom_point(aes(color = group,alpha = 0.5)) + xlab("SNP-ASE BxW") + ylab("RER") + theme(legend.position="none") + scale_color_manual(values = c("darkgreen","purple4","magenta","blue","gray")),type="histogram")
Removed 23257 rows containing missing values (geom_point).
b <- ggExtra::ggMarginal(ggplot(bw.merge2,aes(x=ase.WB.ratio,y=maternal_preference)) + theme_classic() + geom_point(aes(color = group,alpha = 0.5)) + xlab("SNP-ASE WxB") + ylab("RER") + theme(legend.position="none") + scale_color_manual(values = c("darkgreen","purple4","magenta","blue","gray")),type="histogram")
Removed 23935 rows containing missing values (geom_point).
c <- ggExtra::ggMarginal(ggplot(bw.merge2,aes(x=ase.BW.ratio,y=ase.WB.ratio)) + theme_classic() + geom_point(aes(color = group,alpha = 0.5)) + xlab("SNP-ASE WxB") + ylab("SNP-ASE BxW") + theme(legend.position="none") + scale_color_manual(values = c("darkgreen","purple4","magenta","blue","gray")),type="histogram")
Removed 23997 rows containing missing values (geom_point).
grid.arrange(a,b,c,nrow=2)


B73 vs W22
a <- ggplot(bw.merge2,aes(x=ase.BW.ratio,y=maternal_preference)) + geom_bin2d(bins = 50) + theme_classic() + scale_fill_viridis_c(option = "inferno") + xlab("SNP-ASE BxW") + ylab("RER")
b <- ggplot(bw.merge2,aes(x=ase.WB.ratio,y=maternal_preference)) + geom_bin2d(bins = 50) + theme_classic() + scale_fill_viridis_c(option = "inferno") + xlab("SNP-ASE WxB") + ylab("RER")
grid.arrange(a,b,nrow=1)

Compare B73 vs PH207
bp.merged.out <- merge(plot.out2.bp,subset(ase.reads2,ase.reads2$contrast == "BP"),by.x="feature",by.y="gid",all=F)
bp.merged.out2 <- subset(bp.merged.out,bp.merged.out$mean.count >= 10)
a <- ggplot(bp.merged.out2,aes(x=mean.ratio,y=maternal_preference)) + geom_bin2d(bins = 50) + theme_classic() + scale_fill_viridis_c(option = "inferno") + xlab("Mean SNP Maternal Ratio BxW") + ylab("Maternal Preference Ratio")
bp.merged.out3 <- merge(plot.out2.bp,subset(ase.reads2,ase.reads2$contrast == "PB"),by.x="feature",by.y="gid",all=F)
bp.merged.out4 <- subset(bp.merged.out3,bp.merged.out3$mean.count >= 10)
b <- ggplot(bp.merged.out4,aes(x=mean.ratio,y=maternal_preference)) + geom_bin2d(bins = 50) + theme_classic() + scale_fill_viridis_c(option = "inferno") + xlab("Mean SNP Maternal Ratio WxB") + ylab("Maternal Preference Ratio")
grid.arrange(a,b,nrow=1)

Plot just genes
plot.just.genes <- rbind(subset(plot.out2.bw,plot.out2.bw$feature_type == "gene"),subset(plot.out2.bp,plot.out2.bp$feature_type == "gene"),subset(plot.out2.wp,plot.out2.wp$feature_type == "gene"))
ggplot(plot.just.genes,aes(x=imprint,y=maternal_preference,fill=imprint)) + geom_violin() + theme_classic() + theme(panel.grid.major = element_blank(),panel.grid.minor = element_blank()) + facet_grid(genome~contrast) + geom_abline(slope=0, intercept=.33) + geom_abline(slope=0, intercept=.66) + stat_summary(fun.data = give.n, geom = "text")

table(plot.just.genes[,c("genome","contrast","imprint")])
, , imprint = MEG
contrast
genome BP BW WP
B73 138 169 0
PH207 115 0 83
W22 0 121 111
, , imprint = no.imprint
contrast
genome BP BW WP
B73 16253 14882 0
PH207 14611 0 14236
W22 0 14450 15558
, , imprint = PEG
contrast
genome BP BW WP
B73 79 73 0
PH207 77 0 64
W22 0 69 56
meg.genes <- subset(b.both.contrasts,b.both.contrasts$imprint == "MEG")
length(meg.genes$feature[duplicated(meg.genes$feature)])
[1] 0
length(unique(meg.genes$feature))
[1] 202
peg.genes <- subset(b.both.contrasts,b.both.contrasts$imprint == "PEG")
length(peg.genes$feature[duplicated(peg.genes$feature)])
[1] 0
length(unique(peg.genes$feature))
[1] 111
meg.tes <- subset(b.both.contrasts,b.both.contrasts$imprint == "matTE")
length(meg.tes$feature[duplicated(meg.tes$feature)])
[1] 0
length(unique(meg.tes$feature))
[1] 145
Check endosperm vs pericarp expression, especially for MEGs
unfiltered.meg.genes <- subset(b.both.contrasts,b.both.contrasts$feature_type == "gene" & (b.both.contrasts$filter.imprint.BW == "MEG" | b.both.contrasts$filter.imprint.BW == "filtered.MEG"))
imprinted.meg.4heat <- as.matrix(subset(dev.rpm, rownames(dev.rpm) %in% unfiltered.meg.genes$feature & rowMaxs(as.matrix(dev.rpm[,keep.columns])) > 0)[,keep.columns])
pheatmap((imprinted.meg.4heat/rowMaxs(imprinted.meg.4heat)),border_color = NA,fontsize = 8,cluster_cols = F)

a <- subset(imprinted.meg.4heat,rownames(imprinted.meg.4heat) %in% rownames(peri.preferred))/rowMaxs(subset(imprinted.meg.4heat,rownames(imprinted.meg.4heat) %in% rownames(peri.preferred)))
b <- subset(imprinted.meg.4heat,!rownames(imprinted.meg.4heat) %in% rownames(peri.preferred))/rowMaxs(subset(imprinted.meg.4heat,!rownames(imprinted.meg.4heat) %in% rownames(peri.preferred)))
imprinted.meg.4heat.v2 <- rbind(a[order(rowMeans(a),decreasing = T),],b[order(rowMeans(b),decreasing = T),])
pheatmap((imprinted.meg.4heat.v2/rowMaxs(imprinted.meg.4heat.v2)),border_color = NA,fontsize = 8,cluster_cols = F,cluster_rows = F,show_rownames = F,gaps_row = nrow(a))

Check for TEs
peri.te.heat <- as.matrix(subset(dev.rpm, rownames(dev.rpm) %in% meg.tes$feature & rowMaxs(as.matrix(dev.rpm[,keep.columns])) > 0)[,keep.columns])
pheatmap((peri.te.heat/rowMaxs(peri.te.heat)),border_color = NA,fontsize = 8,cluster_cols = F)

peri.te.heat.v2 <- peri.te.heat/rowMaxs(peri.te.heat)
pheatmap((peri.te.heat.v2[order(rowMeans(peri.te.heat.v2),decreasing = T),]),border_color = NA,fontsize = 8,cluster_cols = F,cluster_rows = F,show_rownames = F)

Plot just TEs
plot.just.tes <- rbind(subset(plot.out2.bw,plot.out2.bw$feature_type == "TE"),subset(plot.out2.bp,plot.out2.bp$feature_type == "TE"),subset(plot.out2.wp,plot.out2.wp$feature_type == "TE"))
ggplot(plot.just.tes,aes(x=imprint,y=maternal_preference,fill=imprint)) + geom_violin() + theme_classic() + theme(panel.grid.major = element_blank(),panel.grid.minor = element_blank()) + facet_grid(genome~contrast) + geom_abline(slope=0, intercept=.33) + geom_abline(slope=0, intercept=.66) + stat_summary(fun.data = give.n, geom = "text")

table(plot.just.tes[,c("genome","contrast","filter.imprint")])
, , filter.imprint = MEG
contrast
genome BP BW WP
B73 102 107 0
PH207 94 0 82
W22 0 95 91
, , filter.imprint = no.imprint
contrast
genome BP BW WP
B73 2315 1819 0
PH207 1406 0 1667
W22 0 1773 2636
, , filter.imprint = PEG
contrast
genome BP BW WP
B73 10 7 0
PH207 0 0 2
W22 0 3 5
Make distribution of all based on which libraries got included in DE calls
imp.recips.rpm2 <- data.frame(rbind(subset(imp.BvW.rpm,imp.BvW.rpm$feature %in% rownames(imp.all.de.keep.bw)),subset(imp.BvP.rpm,imp.BvP.rpm$feature %in% rownames(imp.all.de.keep.bp)),subset(imp.WvP.rpm,imp.WvP.rpm$feature %in% rownames(imp.all.de.keep.wp))))
imp.recips.rpm2$A_mean <- rowMeans(imp.recips.rpm2[,1:3])
imp.recips.rpm2$B_mean <- rowMeans(imp.recips.rpm2[,4:6])
imp.recips.rpm2$maternal_preference <- ifelse(imp.recips.rpm2$type == "A",imp.recips.rpm2$A_mean/(imp.recips.rpm2$A_mean + imp.recips.rpm2$B_mean),imp.recips.rpm2$B_mean/(imp.recips.rpm2$A_mean + imp.recips.rpm2$B_mean)) # maternal preferance calculation
ggplot(imp.recips.rpm2,aes(x=genome,y=maternal_preference,fill=genome)) + geom_abline(slope=0, intercept=.33) + geom_abline(slope=0, intercept=.66) + geom_violin() + theme_classic() + theme(panel.grid.major = element_blank(),panel.grid.minor = element_blank()) + facet_wrap(feature_type~contrast,scales="free_x") + stat_summary(fun.data = give.n, geom = "text")

Plot as stacked bars instead
imp.recips.rpm2$mat.category <- factor(ifelse(imp.recips.rpm2$maternal_preference > 0.9, "strong.maternal",
ifelse(imp.recips.rpm2$maternal_preference < 0.1, "strong.paternal",
ifelse(imp.recips.rpm2$maternal_preference > 0.8, "moderate.maternal",
ifelse(imp.recips.rpm2$maternal_preference < 0.2, "moderate.paternal", "biparental")))),
levels = c("strong.maternal","moderate.maternal","biparental","moderate.paternal","strong.paternal"))
imp.recips.rpm2$feature_category <- factor(ifelse(imp.recips.rpm2$feature_type == "TE","TE",
ifelse(imp.recips.rpm2$feature %in% gene.key.BWP$v4_gene_model
| imp.recips.rpm2$feature %in% gene.key.BWP$W22.Zm00004b.1.
| imp.recips.rpm2$feature %in% gene.key.BWP$PH207.Zm00008a.1., "maize.syntenic.gene","maize.nonsyntenic.gene")),
levels=c("maize.syntenic.gene","maize.nonsyntenic.gene","TE"))
stack.mat.category <- data.frame(imp.recips.rpm2 %>% group_by(feature_category,genome,contrast,mat.category) %>% dplyr::summarise(n = n()) %>% dplyr::mutate(proportion = n/sum(n)))
stack.mat.category$sample <- paste(stack.mat.category$genome,stack.mat.category$contrast,sep="_")
ggplot() + geom_bar(aes(x=sample,y=proportion,fill=mat.category),data=stack.mat.category,stat="identity",position = position_fill()) + theme_classic() + scale_fill_manual(values=c("magenta","magenta4","darkgray","navy","blue")) + facet_wrap(.~feature_category,scales="free") + theme(axis.text.x = element_text(angle=90, hjust=1))

proportion.strong - syntenic genes
tmp <- subset(stack.mat.category,stack.mat.category$feature_category == "maize.syntenic.gene" & grepl("strong",stack.mat.category$mat.category))
tmp2 <- data.frame(tmp %>% group_by(sample) %>% dplyr::summarise(strong.parental = sum(proportion)))
mean(tmp2$strong.parental)
[1] 0.02174116
TEs
tmp <- subset(stack.mat.category,stack.mat.category$feature_category == "TE" & grepl("strong",stack.mat.category$mat.category))
tmp2 <- data.frame(tmp %>% group_by(sample) %>% dplyr::summarise(strong.parental = sum(proportion)))
mean(tmp2$strong.parental)
[1] 0.117614
nonsyntenic genes
tmp <- subset(stack.mat.category,stack.mat.category$feature_category == "maize.nonsyntenic.gene" & grepl("strong",stack.mat.category$mat.category))
tmp2 <- data.frame(tmp %>% group_by(sample) %>% dplyr::summarise(strong.parental = sum(proportion)))
mean(tmp2$strong.parental)
[1] 0.06492446
ggplot(imp.recips.rpm2,aes(x=genome,y=maternal_preference,fill=genome)) + geom_abline(slope=0, intercept=.33) + geom_abline(slope=0, intercept=.66) + geom_violin() + theme_classic() + theme(panel.grid.major = element_blank(),panel.grid.minor = element_blank()) + facet_wrap(feature_category~contrast,scales="free_x") + stat_summary(fun.data = give.n, geom = "text")

gene.key.BWP$B.imp.BW <- ifelse(gene.key.BWP$v4_gene_model %in% subset(plot.out2.bw,plot.out2.bw$category != "notDE")[,"feature"],"imprinted",ifelse(gene.key.BWP$v4_gene_model %in% plot.out2.bw$feature,"not.imprinted","NA"))
gene.key.BWP$W.imp.BW <- ifelse(gene.key.BWP$W22.Zm00004b.1. %in% subset(plot.out2.bw,plot.out2.bw$category != "notDE")[,"feature"],"imprinted",ifelse(gene.key.BWP$W22.Zm00004b.1. %in% plot.out2.bw$feature,"not.imprinted","NA"))
gene.key.BWP$B.imp.BP <- ifelse(gene.key.BWP$v4_gene_model %in% subset(plot.out2.bp,plot.out2.bp$category != "notDE")[,"feature"],"imprinted",ifelse(gene.key.BWP$v4_gene_model %in% plot.out2.bp$feature,"not.imprinted","NA"))
gene.key.BWP$P.imp.BP <- ifelse(gene.key.BWP$PH207.Zm00008a.1. %in% subset(plot.out2.bp,plot.out2.bp$category != "notDE")[,"feature"],"imprinted",ifelse(gene.key.BWP$PH207.Zm00008a.1. %in% plot.out2.bp$feature,"not.imprinted","NA"))
gene.key.BWP$P.imp.WP <- ifelse(gene.key.BWP$PH207.Zm00008a.1. %in% subset(plot.out2.wp,plot.out2.wp$category != "notDE")[,"feature"],"imprinted",ifelse(gene.key.BWP$PH207.Zm00008a.1. %in% plot.out2.wp$feature,"not.imprinted","NA"))
gene.key.BWP$W.imp.WP <- ifelse(gene.key.BWP$W22.Zm00004b.1. %in% subset(plot.out2.wp,plot.out2.wp$category != "notDE")[,"feature"],"imprinted",ifelse(gene.key.BWP$W22.Zm00004b.1. %in% plot.out2.wp$feature,"not.imprinted","NA"))
Look at endosperm-specific expression vs constituative expression
endo.seed.columns <- c(names(dev.rpm)[grep("endosperm",names(dev.rpm))],names(dev.rpm)[grep("seed",names(dev.rpm))])
other.columns <- names(dev.rpm)[!names(dev.rpm) %in% endo.seed.columns]
other.columns <- other.columns[order(other.columns)]
endo.preferred <- rownames(subset(dev.rpm,rowSums(dev.rpm[,endo.seed.columns])/rowSums(dev.rpm) > 0.6))
plot.column.order <- c(endo.seed.columns,other.columns)
Summarize features further
b.both.contrasts$expression.type <- ifelse(b.both.contrasts$feature %in% endo.preferred, "endo.preferred","constitutive")
MEG genes
imprinted.meg.4heat <- as.matrix(subset(dev.rpm, rownames(dev.rpm) %in% meg.genes$feature))
a <- subset(imprinted.meg.4heat,rownames(imprinted.meg.4heat) %in% endo.preferred)
b <- subset(imprinted.meg.4heat,!rownames(imprinted.meg.4heat) %in% endo.preferred)
c <- rbind(a,b)
pheatmap((a/rowMaxs(a))[,plot.column.order],border_color = NA,fontsize = 8,cluster_cols = F,clustering_distance_rows = "euclidean")

pheatmap((b/rowMaxs(b))[,plot.column.order],border_color = NA,fontsize = 8,cluster_cols = F,clustering_distance_rows = "euclidean")

pheatmap((c/rowMaxs(c))[,plot.column.order],border_color = NA,fontsize = 8,cluster_cols = F,cluster_rows = F,gaps_col = length(endo.seed.columns),gaps_row = nrow(a),show_rownames = F,show_colnames = F, main = "Developmental Expression of MEGs")

meg.dev <- pheatmap((c/rowMaxs(c))[,plot.column.order],border_color = NA,fontsize = 8,cluster_cols = F,cluster_rows = F,gaps_col = length(endo.seed.columns),gaps_row = nrow(a),show_rownames = F,show_colnames = F, main = "Developmental Expression of MEGs")

PEG genes
imprinted.peg.4heat <- as.matrix(subset(dev.rpm, rownames(dev.rpm) %in% peg.genes$feature))
a <- subset(imprinted.peg.4heat,rownames(imprinted.peg.4heat) %in% endo.preferred)
b <- subset(imprinted.peg.4heat,!rownames(imprinted.peg.4heat) %in% endo.preferred)
c <- rbind(a,b)
pheatmap((a/rowMaxs(a))[,plot.column.order],border_color = NA,fontsize = 8,cluster_cols = F,clustering_distance_rows = "euclidean")

pheatmap((b/rowMaxs(b))[,plot.column.order],border_color = NA,fontsize = 8,cluster_cols = F,clustering_distance_rows = "euclidean")

pheatmap((c/rowMaxs(c))[,plot.column.order],border_color = NA,fontsize = 8,cluster_cols = F,cluster_rows = F,gaps_col = length(endo.seed.columns),gaps_row = nrow(a),show_rownames = F,show_colnames = F, main = "Developmental Expression of PEGs")

peg.dev <- pheatmap((c/rowMaxs(c))[,plot.column.order],border_color = NA,fontsize = 8,cluster_cols = F,cluster_rows = F,gaps_col = length(endo.seed.columns),gaps_row = nrow(a),show_rownames = F,show_colnames = F, main = "Developmental Expression of PEGs")

maternal TEs
imprinted.te.4heat <- as.matrix(subset(dev.rpm, rownames(dev.rpm) %in% meg.tes$feature))
a <- subset(imprinted.te.4heat,rownames(imprinted.te.4heat) %in% endo.preferred)
b <- subset(imprinted.te.4heat,!rownames(imprinted.te.4heat) %in% endo.preferred)
c <- rbind(a,b)
pheatmap((a/rowMaxs(a))[,plot.column.order],border_color = NA,fontsize = 8,cluster_cols = F,clustering_distance_rows = "euclidean")

pheatmap((b/rowMaxs(b))[,plot.column.order],border_color = NA,fontsize = 8,cluster_cols = F,clustering_distance_rows = "euclidean")

pheatmap((c/rowMaxs(c))[,plot.column.order],border_color = NA,fontsize = 8,cluster_cols = F,cluster_rows = F,gaps_col = length(endo.seed.columns),gaps_row = nrow(a),show_rownames = F,show_colnames = T, main = "Developmental Expression of matTEs")

matTE.dev <- pheatmap((c/rowMaxs(c))[,plot.column.order],border_color = NA,fontsize = 8,cluster_cols = F,cluster_rows = F,gaps_col = length(endo.seed.columns),gaps_row = nrow(a),show_rownames = F,show_colnames = T, main = "Developmental Expression of matTEs")

Stacked bar with imprinted features, syntelog, expression.type
of.interest <- subset(b.both.contrasts,b.both.contrasts$imprint != "no.imprint")
of.interest2 <- data.frame(of.interest %>% group_by(imprint,syntelog,expression.type) %>% dplyr::summarise(features = n()))
of.interest2$type <- factor(ifelse(of.interest2$imprint == "matTE", "matTE",paste(of.interest2$imprint,of.interest2$syntelog,sep="_")),
levels=c("PEG_conserved.maize","PEG_variable","MEG_conserved.maize","MEG_variable","matTE"))
of.interest2$expression.type <- factor(of.interest2$expression.type,levels=c("endo.preferred","constitutive"))
ggplot() + geom_bar(aes(x=type,y=features,fill=expression.type),data=of.interest2,stat="identity",position = position_fill()) + theme_classic() + theme(axis.text.x = element_text(angle=90, hjust=1)) + scale_fill_manual(values=c("darkorange","honeydew4"))

Binding sites Find binding sites from Batista et al. in B73 Convert sites to meme file module load meme/4.12.0-py2-openmpi3-qbrm5zn iupac2meme TTWCCATATW > batista_motif1_meme.txt iupac2meme CCAWAAATGG > batista_motif2_meme.txt cat batista_motif1_meme.txt batista_motif2_meme.txt > batista_motifs_meme.txt module load bedtools2/2.27.1-s2mtpsu cut -f 1,4,5,9 B73.structuralTEv2.2018.12.20.filteredTE.disjoined.gff3 | bedtools getfasta -name -fi Zea_mays.AGPv4.dna.toplevel.fa -bed - -fo B73v4.disjoinedTE.fa fimo batista_motifs_meme.txt B73v4.disjoinedTE.fa > fimo.txt cp fimo_out/fimo.txt batista_motifs_fimo_out.txt
grep gene Zea_mays.AGPv4.33.gff3 | cut -f 1,4,5,9 - | bedtools getfasta -name -fi Zea_mays.AGPv4.dna.toplevel.fa -bed - -fo B73v4.gene.fa fimo batista_motifs_meme.txt B73v4.gene.fa cp fimo_out/fimo.txt batista_motifs_fimo_gene_out.txt
all.sites <- read.table("batista_motifs_fimo_out.txt",header=F,stringsAsFactors = F,sep="\t")
all.sites$te <- substr(all.sites$V3,4,24)
sites.per.te <- data.frame(all.sites %>% group_by(te) %>% dplyr::summarise(motif1 = sum(V1 == "TTWCCATATW"),motif2 = sum(V1 == "CCAWAAATGG")))
sites.per.te$motif.type <- ifelse(sites.per.te$motif1 > 0 & sites.per.te$motif2 > 0, "both",
ifelse(sites.per.te$motif1 > 0, "motif1","motif2"))
names(sites.per.te)[1] <- "feature"
sites.per.te$feature_type <- "TE"
Gene binding sites
gene.sites <- read.table("batista_motifs_fimo_gene_out.txt",header=F,stringsAsFactors = F,sep="\t")
gene.sites$gene <- substr(gene.sites$V3,4,17)
gene.sites2 <- subset(gene.sites,!grepl("GRMZM",gene.sites$gene))
sites.per.gene <- data.frame(gene.sites2 %>% group_by(gene) %>% dplyr::summarise(motif1 = sum(V1 == "TTWCCATATW"),motif2 = sum(V1 == "CCAWAAATGG")))
sites.per.gene$motif.type <- ifelse(sites.per.gene$motif1 > 0 & sites.per.gene$motif2 > 0, "both",
ifelse(sites.per.gene$motif1 > 0, "motif1","motif2"))
names(sites.per.gene)[1] <- "feature"
sites.per.gene$feature_type <- "gene"
sites.per.feature <- data.frame(rbind(sites.per.gene,sites.per.te))
#write.table(sites.per.feature,file="PHE1_binding_sites_genesTEs.txt",quote=F,sep="\t",row.names = F)
sites.per.feature <- read.table("PHE1_binding_sites_genesTEs.txt",sep = "\t",stringsAsFactors = F,header=T)
b.both.contrasts$motif1 <- b.both.contrasts$feature %in% subset(sites.per.feature,sites.per.feature$motif1 > 0)[,"feature"]
b.both.contrasts$motif2 <- b.both.contrasts$feature %in% subset(sites.per.feature,sites.per.feature$motif2 > 0)[,"feature"]
b.both.contrasts$motif.type <- ifelse(b.both.contrasts$motif1 & b.both.contrasts$motif2, "both",
ifelse(b.both.contrasts$motif1, "motif1",ifelse(b.both.contrasts$motif2,"motif2","no.motif")))
table(b.both.contrasts$motif.type)
both motif1 motif2 no.motif
3235 3824 3573 10116
sites.summary <- data.frame(b.both.contrasts %>% group_by(imprint,motif.type) %>% dplyr::summarise(n = n()) %>% dplyr::mutate(freq = n/sum(n)))
ggplot() + geom_bar(aes(x=imprint,y=n,fill=motif.type),data=sites.summary,stat="identity",position = position_fill()) + theme_classic() + theme(axis.text.x = element_text(angle=90, hjust=1)) + scale_fill_brewer(palette = "YlGnBu")

ggplot() + geom_bar(aes(x=imprint,y=freq,fill=motif.type),data=sites.summary,stat="identity",position = position_dodge()) + theme_classic() + theme(axis.text.x = element_text(angle=90, hjust=1)) + scale_fill_brewer(palette = "YlGnBu")

b.both.contrasts$either.motif <- ifelse(b.both.contrasts$motif.type == "no.motif","no.motif","has.motif")
sites.summary2 <- data.frame(b.both.contrasts %>% group_by(imprint,either.motif) %>% dplyr::summarise(n = n()) %>% dplyr::mutate(freq = n/sum(n)))
sites.summary2
imprint either.motif n freq
1 matTE has.motif 73 0.5034483
2 matTE no.motif 72 0.4965517
3 MEG has.motif 96 0.4752475
4 MEG no.motif 106 0.5247525
5 no.imprint has.motif 10394 0.5122721
6 no.imprint no.motif 9896 0.4877279
7 PEG has.motif 69 0.6216216
8 PEG no.motif 42 0.3783784
Look at per TE fam
te.both.contrasts <- subset(b.both.contrasts,b.both.contrasts$imprint == "matTE")
te.both.contrasts$fam <- substr(te.both.contrasts$feature,0,8)
tmp <- data.frame(table(te.both.contrasts$fam))
tmp2 <- subset(tmp,tmp$Freq>1)
site.te.summary <- data.frame(subset(te.both.contrasts,te.both.contrasts$fam %in% tmp2$Var1) %>% group_by(motif.type,fam) %>% dplyr::summarise(features = n()))
ggplot() + geom_bar(aes(x=fam,y=features,fill=motif.type),data=site.te.summary,stat="identity",position = position_fill()) + theme_classic() + theme(axis.text.x = element_text(angle=90, hjust=1))

DHH2
DHH2.both.contrasts <- subset(b.both.contrasts,grepl("DHH00002",b.both.contrasts$feature))
DHH2.not.expressed <- subset(imp.all,grepl("DHH00002Zm00001d",imp.all$ID) & !imp.all$ID %in% b.both.contrasts$feature)
DHH2.not.expressed2 <- data.frame(matrix(ncol=ncol(DHH2.both.contrasts),nrow=nrow(DHH2.not.expressed)))
colnames(DHH2.not.expressed2) <- colnames(DHH2.both.contrasts)
DHH2.not.expressed2$feature <- DHH2.not.expressed$ID
DHH2.not.expressed2$imprint <- "not.detected"
DHH2.both.contrasts2 <- rbind(DHH2.both.contrasts,DHH2.not.expressed2)
DHH2.both.sites <- merge(DHH2.both.contrasts2,sites.per.feature,by="feature",all.x=T)
names(DHH2.both.sites) <- gsub(".y",".sites",names(DHH2.both.sites),fixed = T)
DHH2.both.sites$motif1.sites[is.na(DHH2.both.sites$motif1.sites)] <- 0
DHH2.both.sites$motif2.sites[is.na(DHH2.both.sites$motif2.sites)] <- 0
DHH2.both.sites$motif.type <- ifelse(DHH2.both.sites$motif1.sites > 0 & DHH2.both.sites$motif2.sites > 0, "both",
ifelse(DHH2.both.sites$motif1.sites > 0, "motif1",ifelse(DHH2.both.sites$motif2.sites > 0,"motif2","no.motif")))
site.dhh.summary <- data.frame(DHH2.both.sites %>% group_by(motif.type,imprint) %>% dplyr::summarise(features = n()))
ggplot() + geom_bar(aes(x=imprint,y=features,fill=motif.type),data=site.dhh.summary,stat="identity",position = position_fill()) + theme_classic() + theme(axis.text.x = element_text(angle=90, hjust=1)) + scale_fill_brewer(palette = "YlGnBu")

ggplot(DHH2.both.sites,aes(x=imprint,y=motif1.sites,fill=imprint)) + geom_boxplot() + theme_classic() + theme(panel.grid.major = element_blank(),panel.grid.minor = element_blank()) + stat_summary(fun.data = give.n, geom = "text")

ggplot(DHH2.both.sites,aes(x=imprint,y=motif2.sites,fill=imprint)) + geom_boxplot() + theme_classic() + theme(panel.grid.major = element_blank(),panel.grid.minor = element_blank()) + stat_summary(fun.data = give.n, geom = "text")

b.both.contrasts$imprint.syntelog <- paste(b.both.contrasts$imprint,b.both.contrasts$syntelog,sep="_")
gene.key.syntenic.grasses <- subset(gene.key,nchar(gene.key$sorghum_ortholog) > 0 & nchar(gene.key$foxtail_millet_ortholog) > 0 & nchar(gene.key$rice_ortholog) > 0 & nchar(gene.key$brachypodium_ortholog) > 0)
b.both.contrasts$syntenic.grasses <- ifelse(b.both.contrasts$feature_type == "TE","NA.TE",ifelse(b.both.contrasts$feature %in% gene.key.syntenic.grasses$v4_gene_model, "syntenic.grasses","nonsyntenic.grasses"))
table(b.both.contrasts[,c("imprint","syntenic.grasses","syntelog")])
, , syntelog = conserved.maize
syntenic.grasses
imprint NA.TE nonsyntenic.grasses syntenic.grasses
matTE 0 0 0
MEG 0 30 24
no.imprint 0 4229 9490
PEG 0 35 52
, , syntelog = variable
syntenic.grasses
imprint NA.TE nonsyntenic.grasses syntenic.grasses
matTE 145 0 0
MEG 0 134 14
no.imprint 2712 2528 1331
PEG 0 21 3
summarize.synteny1 <- data.frame(subset(b.both.contrasts,b.both.contrasts$feature_type == "gene") %>% group_by(imprint,syntelog) %>% dplyr::summarize(n=n()) %>% dplyr::mutate(freq=n/sum(n)))
a <- ggplot() + geom_bar(aes(x=imprint,y=freq,fill=syntelog),data=subset(summarize.synteny1,summarize.synteny1$syntelog == "PAV"),stat="identity",position = position_dodge()) + theme_classic() + scale_fill_manual(values = c("green")) + theme(axis.text.x = element_text(angle=90, hjust=1)) + ggtitle("Nonsyntenic within maize")+ theme(legend.position = "none") + ylim(c(0,1))
summarize.synteny2 <- data.frame(subset(b.both.contrasts,b.both.contrasts$feature_type == "gene") %>% group_by(imprint,syntenic.grasses) %>% dplyr::summarize(n=n()) %>% dplyr::mutate(freq=n/sum(n)))
b <- ggplot() + geom_bar(aes(x=imprint,y=freq,fill=syntenic.grasses),data=subset(summarize.synteny2,summarize.synteny2$syntenic.grasses == "nonsyntenic.grasses"),stat="identity",position = position_dodge()) + theme_classic() + scale_fill_manual(values = c("forestgreen")) + theme(axis.text.x = element_text(angle=90, hjust=1)) + ggtitle("Nonsyntenic within grasses")+ theme(legend.position = "none") + ylim(c(0,1))
grid.arrange(a,b,nrow=1)

How many imprinted genes have GO terms? File downloaded from AGRIGO using link: http://systemsbiology.cau.edu.cn/agriGOv2/download/958_slimGO on 20 Feb 2020
gene.go <- read.table("B73v4_slimGO.txt",header=F,sep="\t",stringsAsFactors = F)
gene.go <- gene.go[,3:4]
names(gene.go) <- c("gene","term")
b.both.contrasts$has.go.term <- factor(ifelse(b.both.contrasts$feature_type == "TE", "NA.TE",
ifelse(b.both.contrasts$feature %in% gene.go$gene, "GO.term(s)","no.GO")),levels=c("GO.term(s)","no.GO"))
summarize.go <- data.frame(subset(b.both.contrasts,b.both.contrasts$feature_type == "gene") %>% group_by(imprint.syntelog,has.go.term,) %>% dplyr::summarise(n = n()) %>% dplyr::mutate(freq = n/sum(n)))
ggplot() + geom_bar(aes(x=imprint.syntelog,y=freq,fill=has.go.term),data=subset(summarize.go,summarize.go$has.go.term == "no.GO"),stat="identity",position = position_dodge()) + theme_classic() + scale_fill_manual(values = c("purple")) + theme(axis.text.x = element_text(angle=90, hjust=1)) + ggtitle("Genes without GO term")+ theme(legend.position = "none")

b.both.contrasts.genes <- subset(b.both.contrasts,b.both.contrasts$feature_type == "gene")
b.both.alluv <- data.frame(subset(b.both.contrasts.genes,b.both.contrasts.genes$imprint != "no.imprint") %>% group_by(imprint,syntelog,syntenic.grasses,has.go.term) %>% dplyr::summarise(n=n()))
ggplot(data=b.both.alluv,
aes(axis4 = imprint,axis3=syntelog,axis2=syntenic.grasses,axis1=has.go.term,y=n)) +
scale_x_discrete(limits = rev(c("imprint","syntelog", "syntenic.grasses", "has.go.term")), expand = c(.1, .05)) +
xlab("features") +
geom_alluvium(aes(fill = imprint)) +
geom_stratum() + geom_text(stat = "stratum", infer.label = TRUE) + coord_flip()+
theme_classic() + scale_fill_manual(values=c("magenta3","navy"))

b.both.alluv2 <- data.frame(subset(b.both.contrasts.genes,b.both.contrasts.genes$imprint != "no.imprint") %>% group_by(imprint,syntelog,has.go.term) %>% dplyr::summarise(n=n()))
ggplot(data=b.both.alluv2,
aes(axis3 = imprint,axis2=syntelog,axis1=has.go.term,y=n)) +
scale_x_discrete(limits = rev(c("imprint","syntelog", "has.go.term")), expand = c(.1, .05)) +
xlab("features") +
geom_alluvium(aes(fill = imprint)) +
geom_stratum() + geom_text(stat = "stratum", infer.label = TRUE) + coord_flip()+
theme_classic() + scale_fill_manual(values=c("magenta3","navy"))

b.both.alluv2b <- data.frame(b.both.contrasts.genes %>% group_by(imprint,syntenic.grasses,has.go.term,expression.type) %>% dplyr::summarise(n=n()))
b.both.alluv2b$syntenic.grasses <- factor(b.both.alluv2b$syntenic.grasses,levels=c("nonsyntenic.grasses","syntenic.grasses"))
b.both.alluv2b$has.go.term <- factor(b.both.alluv2b$has.go.term,levels=c("no.GO","GO.term(s)"))
b.both.alluv2b$expression.type <- factor(b.both.alluv2b$expression.type,levels=c("endo.preferred","constitutive"))
ggplot(data=b.both.alluv2b,
aes(axis4 = imprint,axis3=syntenic.grasses,axis2=has.go.term,axis1=expression.type,y=n)) +
scale_x_discrete(limits = rev(c("imprint","syntenic.grasses", "has.go.term","expression.type")), expand = c(.1, .05)) +
xlab("features") +
geom_alluvium(aes(fill = syntenic.grasses)) +
geom_stratum() + geom_text(stat = "stratum", infer.label = TRUE) + coord_flip()+
theme_classic() + scale_fill_manual(values=c("green","steelblue")) + facet_grid(.~imprint,scales="free")

Stats for above - chisquared test
m.stats <- subset(b.both.alluv2b,b.both.alluv2b$imprint %in% c("MEG","no.imprint"))
a <- data.frame(m.stats %>% group_by(imprint,syntenic.grasses) %>% dplyr::summarise(n=sum(n)) %>% pivot_wider(names_from = syntenic.grasses, values_from = n))
a$percent <- a$syntenic.grasses/rowSums(a[,2:3])
chisq.test(a[,2:3])
Pearson's Chi-squared test with Yates' continuity correction
data: a[, 2:3]
X-squared = 151.71, df = 1, p-value < 2.2e-16
b <- data.frame(m.stats %>% group_by(imprint,has.go.term) %>% dplyr::summarise(n=sum(n)) %>% pivot_wider(names_from = has.go.term, values_from = n))
b$percent <- b$no.GO/rowSums(b[,2:3])
chisq.test(b[,2:3])
Pearson's Chi-squared test with Yates' continuity correction
data: b[, 2:3]
X-squared = 97.181, df = 1, p-value < 2.2e-16
c <- data.frame(m.stats %>% group_by(imprint,expression.type) %>% dplyr::summarise(n=sum(n)) %>% pivot_wider(names_from = expression.type, values_from = n))
c$percent <- c$endo.preferred/rowSums(c[,2:3])
chisq.test(c[,2:3])
Pearson's Chi-squared test with Yates' continuity correction
data: c[, 2:3]
X-squared = 2681.2, df = 1, p-value < 2.2e-16
p.stats <- subset(b.both.alluv2b,b.both.alluv2b$imprint %in% c("PEG","no.imprint"))
d <- data.frame(p.stats %>% group_by(imprint,syntenic.grasses) %>% dplyr::summarise(n=sum(n)) %>% pivot_wider(names_from = syntenic.grasses, values_from = n))
d$percent <- d$syntenic.grasses/rowSums(d[,2:3])
chisq.test(d[,2:3])
Pearson's Chi-squared test with Yates' continuity correction
data: d[, 2:3]
X-squared = 6.2213, df = 1, p-value = 0.01262
e <- data.frame(p.stats %>% group_by(imprint,has.go.term) %>% dplyr::summarise(n=sum(n)) %>% pivot_wider(names_from = has.go.term, values_from = n))
e$percent <- e$no.GO/rowSums(e[,2:3])
chisq.test(e[,2:3])
Pearson's Chi-squared test with Yates' continuity correction
data: e[, 2:3]
X-squared = 2.4303, df = 1, p-value = 0.119
f <- data.frame(p.stats %>% group_by(imprint,expression.type) %>% dplyr::summarise(n=sum(n)) %>% pivot_wider(names_from = expression.type, values_from = n))
f$percent <- f$endo.preferred/rowSums(f[,2:3])
chisq.test(f[,2:3])
Chi-squared approximation may be incorrect
Pearson's Chi-squared test with Yates' continuity correction
data: f[, 2:3]
X-squared = 263.19, df = 1, p-value < 2.2e-16
Version 2
b.both.alluv2b <- data.frame(b.both.contrasts.genes %>% group_by(imprint,syntelog,syntenic.grasses,expression.type) %>% dplyr::summarise(n=n()))
b.both.alluv2b$syntenic.grasses <- factor(b.both.alluv2b$syntenic.grasses,levels=c("nonsyntenic.grasses","syntenic.grasses"))
b.both.alluv2b$syntelog <- factor(b.both.alluv2b$syntelog,levels=c("variable","conserved.maize"))
b.both.alluv2b$expression.type <- factor(b.both.alluv2b$expression.type,levels=c("endo.preferred","constitutive"))
ggplot(data=b.both.alluv2b,
aes(axis4 = imprint,axis3 = syntelog,axis2=syntenic.grasses,axis1=expression.type,y=n)) +
scale_x_discrete(limits = rev(c("imprint","syntelog","syntenic.grasses","expression.type")), expand = c(.1, .05)) +
xlab("features") +
geom_alluvium(aes(fill = syntelog)) +
geom_stratum() + geom_text(stat = "stratum", infer.label = TRUE) + coord_flip()+
theme_classic() + scale_fill_manual(values=c("green","steelblue")) + facet_grid(.~imprint,scales="free")

m.stats <- subset(b.both.alluv2b,b.both.alluv2b$imprint %in% c("MEG","no.imprint"))
p.stats <- subset(b.both.alluv2b,b.both.alluv2b$imprint %in% c("PEG","no.imprint"))
a <- data.frame(m.stats %>% group_by(imprint,syntelog) %>% dplyr::summarise(n=sum(n)) %>% pivot_wider(names_from = syntelog, values_from = n))
chisq.test(a[,2:3])
Pearson's Chi-squared test with Yates' continuity correction
data: a[, 2:3]
X-squared = 298.28, df = 1, p-value < 2.2e-16
d <- data.frame(p.stats %>% group_by(imprint,syntelog) %>% dplyr::summarise(n=sum(n)) %>% pivot_wider(names_from = syntelog, values_from = n))
chisq.test(d[,2:3])
Pearson's Chi-squared test with Yates' continuity correction
data: d[, 2:3]
X-squared = 3.0734e-26, df = 1, p-value = 1
head(subset(b.both.contrasts.genes,b.both.contrasts.genes$imprint == "MEG" & b.both.contrasts.genes$syntenic.grasses == "nonsyntenic.grasses" & b.both.contrasts.genes$has.go.term == "no.GO"))
feature filter.imprint.BW syntelog.BW imprinted.both.BW filter.imprint.BP syntelog.BP feature_type
2971 Zm00001d001971 MEG non.syntelog non.syntelog no.imprint non.syntelog gene
3366 Zm00001d002991 MEG non.syntelog non.syntelog MEG non.syntelog gene
3427 Zm00001d003158 MEG non.syntelog non.syntelog MEG non.syntelog gene
3730 Zm00001d004147 MEG non.syntelog non.syntelog MEG non.syntelog gene
3769 Zm00001d004412 MEG non.syntelog non.syntelog not.detected NA gene
4088 Zm00001d005617 no.imprint non.syntelog non.syntelog MEG non.syntelog gene
imprint syntelog agreement expression.type motif1 motif2 motif.type either.motif imprint.syntelog
2971 MEG variable mismatched_call endo.preferred FALSE FALSE no.motif no.motif MEG_variable
3366 MEG variable matched.call endo.preferred TRUE TRUE both has.motif MEG_variable
3427 MEG variable matched.call endo.preferred FALSE FALSE no.motif no.motif MEG_variable
3730 MEG variable matched.call endo.preferred TRUE TRUE both has.motif MEG_variable
3769 MEG variable mismatched_call endo.preferred FALSE FALSE no.motif no.motif MEG_variable
4088 MEG variable mismatched_call endo.preferred FALSE FALSE no.motif no.motif MEG_variable
syntenic.grasses has.go.term
2971 nonsyntenic.grasses no.GO
3366 nonsyntenic.grasses no.GO
3427 nonsyntenic.grasses no.GO
3730 nonsyntenic.grasses no.GO
3769 nonsyntenic.grasses no.GO
4088 nonsyntenic.grasses no.GO
matTE things.
b.both.te <- subset(b.both.contrasts,b.both.contrasts$feature_type == "TE")
shared.BWP <- subset(te.anno.compare, te.anno.compare$B73 == "present" & nchar(te.anno.compare$B73.annotation == 21) & te.anno.compare$W22 == "present" & nchar(te.anno.compare$W22.annotation == 21)& te.anno.compare$PH207 == "present" & nchar(te.anno.compare$PH207.annotation == 21))
b.both.te$shared.TE.BW <- ifelse(b.both.te$feature %in% shared.BW$B73.annotation, "shared.in.BW","variable.BW")
b.both.te$shared.TE.BP <- ifelse(b.both.te$feature %in% shared.bp$B73.annotation, "shared.in.BP","variable.BP")
b.both.te$shared.TE <- ifelse(b.both.te$feature %in% shared.BWP$B73.annotation, "shared.in.all","variable")
mat.tes <- subset(b.both.te,b.both.te$imprint == "matTE")
mat.tes$order <- substr(mat.tes$feature,0,2)
table(mat.tes[,c("order","shared.TE")])
shared.TE
order shared.in.all variable
DH 4 52
DT 0 9
RI 0 2
RL 6 72
mat.tes$fam <- substr(mat.tes$feature,0,8)
tmp <- data.frame(table(mat.tes$fam))
nrow(tmp)
[1] 84
subset(tmp,tmp$Freq > 1)
Var1 Freq
1 DHH00001 3
2 DHH00002 44
5 DHH00015 3
21 RLC00002 5
22 RLC00004 3
34 RLG00001 4
35 RLG00003 4
36 RLG00005 3
b.both.tes <- subset(b.both.contrasts,b.both.contrasts$feature_type == "TE")
b.both.tes$order <- factor(substr(b.both.tes$feature,0,2),levels=c("DH","DT","RL","RI","RS"))
b.both.tes$shared.TE <- ifelse(b.both.tes$feature %in% shared.BWP$B73.annotation, "shared.in.all","variable")
mat.tes.alluv <- data.frame(subset(b.both.tes,b.both.tes$order %in% c("RL","DT","DH")) %>% group_by(imprint,order,shared.TE,expression.type) %>% dplyr::summarise(n = n()))
ggplot(data=mat.tes.alluv,
aes(axis4 = imprint, axis3 = order,axis2=shared.TE,axis1=expression.type,y=n)) +
scale_x_discrete(limits = rev(c("imprint","order","shared.TE", "expression.type")), expand = c(.1, .05)) +
xlab("features") +
geom_alluvium(aes(fill = order)) +
geom_stratum() + geom_text(stat = "stratum", infer.label = TRUE) + coord_flip()+
theme_classic() + facet_grid(.~imprint,scales="free") + scale_fill_manual(values=c("steelblue","green","darkorchid3"))

mat.tes.alluv <- data.frame(b.both.tes %>% group_by(imprint,order,shared.TE,expression.type) %>% dplyr::summarise(n = n()))
ggplot(data=subset(mat.tes.alluv,mat.tes.alluv$imprint == "matTE"),
aes(axis3 = order,axis2=shared.TE,axis1=expression.type,y=n)) +
scale_x_discrete(limits = rev(c("order","shared.TE", "expression.type")), expand = c(.1, .05)) +
xlab("features") +
geom_alluvium(aes(fill = order)) +
geom_stratum() + geom_text(stat = "stratum", infer.label = TRUE) +
theme_classic() + scale_fill_manual(values=c("steelblue","green","darkorchid3","hotpink")) + coord_flip()

B73.te.attributes <- read.table("B73_filteredTE_combined_attributes_12.20.18.txt",header=T,stringsAsFactors = F,sep="\t")
B73.te.attributes$imprint <- ifelse(B73.te.attributes$TE %in% mat.tes$feature, "matTE",
ifelse(B73.te.attributes$TE %in% b.both.contrasts$feature, "no.imprint","not.detected"))
LTR.age <- subset(B73.te.attributes,!is.na(B73.te.attributes$LTRsimilarity))
ggplot(LTR.age,aes(x=imprint,y=LTRsimilarity,fill=imprint))+ geom_abline(slope=0, intercept=.33) + geom_abline(slope=0, intercept=.66) + geom_violin() + theme_classic() + theme(panel.grid.major = element_blank(),panel.grid.minor = element_blank()) + stat_summary(fun.data = give.n, geom = "text")

ggplot(subset(B73.te.attributes,B73.te.attributes$order %in% c("RL","DT","DH")),aes(x=imprint,y=disjoined,fill=imprint))+ geom_abline(slope=0, intercept=.33) + geom_abline(slope=0, intercept=.66) + geom_boxplot() + theme_classic() + theme(panel.grid.major = element_blank(),panel.grid.minor = element_blank()) + stat_summary(fun.data = give.n, geom = "text") + facet_wrap(.~order,scales = "free")

Tissue-specific expression
table(b.both.te[,c("imprint","expression.type")])
expression.type
imprint constitutive endo.preferred
matTE 12 133
no.imprint 1247 1465
133/(133 + 12)
[1] 0.9172414
1465/(1465 + 1247)
[1] 0.5401917
are maternal TEs enriched for their closest gene being a MEG?
# write.table(meg.tes[,1,drop=F],file="matTEs.txt",quote=F,row.names = F,col.names = F)
Find closest gene to matTEs
module load bedtools2/2.27.1-s2mtpsu sort -k 1,1 -k4,4n matTEs.gff3 | bedtools closest -D b -t all -wa -wb -a - -b /work/LAS/sna-lab/sna/B74v4/Zea_mays.AGPv4.33.gene.gff3 > matTE_closest_gene_v2.bed cut -f 9,18,19 matTE_closest_gene_v2.bed > matTE_closest_gene_v2.txt
matTE_closest_gene <- read.table("matTE_closest_gene_v2.txt",header=F,stringsAsFactors = F,sep="\t")
matTE_closest_gene$TE <- substr(matTE_closest_gene$V1,4,24)
matTE_closest_gene$gene <- substr(matTE_closest_gene$V2,4,17)
matTE_closest_gene$distance <- matTE_closest_gene$V3
matTE_closest_gene$TE.type <- ifelse(matTE_closest_gene$TE %in% meg.tes$feature,"matTE","No.Imprint")
matTE_closest_gene$gene.type <- ifelse(matTE_closest_gene$gene %in% meg.genes$feature,"MEG",
ifelse(matTE_closest_gene$gene %in% peg.genes$feature, "PEG","No.Imprint"))
matTE_closest_gene$distance.cat <- ifelse(matTE_closest_gene$distance == 0, "overlap",
ifelse(abs(matTE_closest_gene$distance) < 5000, "near","far"))
table(matTE_closest_gene[,c("gene.type","distance.cat")])
distance.cat
gene.type far near overlap
MEG 3 9 7
No.Imprint 47 43 45
table(matTE_closest_gene[,c("gene.type","TE.type")])
TE.type
gene.type matTE
MEG 19
No.Imprint 135
Binomial test - are matTEs enriched for having MEG as nearest gene?
successes <- nrow(subset(matTE_closest_gene,matTE_closest_gene$gene.type == "MEG"))
trials <- length(unique(matTE_closest_gene$TE))
proportion.genes.MEG <- nrow(subset(b.both.contrasts.genes,b.both.contrasts.genes$imprint == "MEG"))/nrow(b.both.contrasts.genes)
binom.test(successes,trials,proportion.genes.MEG,alternative = "greater")
Exact binomial test
data: successes and trials
number of successes = 19, number of trials = 145, p-value = 7.211e-15
alternative hypothesis: true probability of success is greater than 0.01129059
95 percent confidence interval:
0.08753504 1.00000000
sample estimates:
probability of success
0.1310345
successes.peg <- 0
proportion.genes.PEG <- nrow(subset(b.both.contrasts.genes,b.both.contrasts.genes$imprint == "PEG"))/nrow(b.both.contrasts.genes)
binom.test(successes.peg,trials,proportion.genes.PEG,alternative = "less")
Exact binomial test
data: successes.peg and trials
number of successes = 0, number of trials = 145, p-value = 0.4056
alternative hypothesis: true probability of success is less than 0.006204237
95 percent confidence interval:
0.00000000 0.02044826
sample estimates:
probability of success
0
plot.test <- data.frame(matrix(NA,nrow=4,ncol=3))
names(plot.test) <- c("set","imprint","freq")
plot.test$imprint <- c("MEG","MEG","PEG","PEG")
plot.test$set <- c("MEG.success","proportion.MEG","PEG.success","proportion.PEG")
plot.test$freq <- c(successes/trials,proportion.genes.MEG,0,proportion.genes.PEG)
ggplot() + geom_bar(aes(x=set,y=freq,fill="red"),data=plot.test,stat="identity",position = "identity", show.legend = FALSE) + theme_minimal() + ylim(0,0.15) + theme(axis.text.x = element_text(angle=90, hjust=1),panel.grid.major.x = element_blank()) + facet_wrap(imprint~.,scales="free_x")

Make version with Ns and where upstream and downstream are shown
matTE.MEG <- subset(matTE_closest_gene,matTE_closest_gene$gene.type == "MEG")[,c("TE","gene","distance","TE.type","gene.type")]
plot.test2 <- plot.test[2:4,1:2]
plot.test2$count <- c(proportion.genes.MEG * trials,0,proportion.genes.PEG * trials)
plot.test2$group <- c("expected","PEG","expected")
plot.test2[4,] <- c("MEG.success","MEG",nrow(subset(matTE.MEG,matTE.MEG$distance < 0)),"upstream")
plot.test2[5,] <- c("MEG.success","MEG",nrow(subset(matTE.MEG,matTE.MEG$distance == 0)),"overlap")
plot.test2[6,] <- c("MEG.success","MEG",nrow(subset(matTE.MEG,matTE.MEG$distance > 0)),"downstream")
plot.test2$count <- as.numeric(plot.test2$count)
plot.test2$group <- factor(plot.test2$group,levels=c("upstream","overlap","downstream","expected","PEG"))
plot.test2$set <- factor(c("MEG.expected","PEG.nearest","PEG.expected","MEG.nearest","MEG.nearest","MEG.nearest"),levels=c("MEG.nearest","MEG.expected","PEG.nearest","PEG.expected"))
ggplot() + geom_bar(aes(x=set,y=count,fill=group),color="black",data=plot.test2,stat="identity",position = "identity") + theme_minimal() + theme(axis.text.x = element_text(angle=90, hjust=1),panel.grid.major.x = element_blank()) + facet_wrap(imprint~.,scales="free_x") + scale_fill_manual(values=c("indianred1","honeydew","lightblue3","black","yellow")) + scale_y_continuous(breaks=c(0,2,4,6,8,10),limits = c(0,11))

Table of matTE nearest gene is MEG
matTE.MEG$order <- substr(matTE.MEG$TE,0,3)
for(i in 1:nrow(matTE.MEG)){
if(matTE.MEG[i,"gene"] %in% plot.out2.bw$feature){
matTE.MEG[i,"ratio.BW.gene"] <- subset(plot.out2.bw,plot.out2.bw$feature == matTE.MEG[i,"gene"])[,"maternal_preference"]
}
else(matTE.MEG[i,"ratio.BW.gene"] <- "NA")
if(matTE.MEG[i,"gene"] %in% plot.out2.bp$feature){
matTE.MEG[i,"ratio.BP.gene"] <- subset(plot.out2.bp,plot.out2.bp$feature == matTE.MEG[i,"gene"])[,"maternal_preference"]
}
else(matTE.MEG[i,"ratio.BP.gene"] <- "NA")
if(matTE.MEG[i,"TE"] %in% plot.out2.bw$feature){
matTE.MEG[i,"ratio.BW.TE"] <- subset(plot.out2.bw,plot.out2.bw$feature == matTE.MEG[i,"TE"])[,"maternal_preference"]
}
else(matTE.MEG[i,"ratio.BW.TE"] <- "NA")
if(matTE.MEG[i,"TE"] %in% plot.out2.bp$feature){
matTE.MEG[i,"ratio.BP.TE"] <- subset(plot.out2.bp,plot.out2.bp$feature == matTE.MEG[i,"TE"])[,"maternal_preference"]
}
else(matTE.MEG[i,"ratio.BP.TE"] <- "NA")
matTE.MEG[i,"gene.variability"] <- subset(b.both.contrasts,b.both.contrasts$feature == matTE.MEG[i,"gene"])[,"syntelog"]
matTE.MEG[i,"gene.expression"] <- subset(b.both.contrasts,b.both.contrasts$feature == matTE.MEG[i,"gene"])[,"expression.type"]
matTE.MEG[i,"TE.expression"] <- subset(b.both.contrasts,b.both.contrasts$feature == matTE.MEG[i,"TE"])[,"expression.type"]
}
#write.table(matTE.MEG[order(matTE.MEG$distance),],file="matTE_closest_gene_is_MEG_v2.txt",quote=F,sep="\t",row.names = F)
matTE.MEG2 <- merge(matTE.MEG,te.anno.compare,by="TE",all.x=T)
Output files
# write.table(b.both.contrasts,file="B73_features_both_contrasts.out.txt",sep="\t",quote=F,row.names = F)
# names(plot.out2.bw)[14] <- "RER"
# plot.out2.bw$imprint <- NULL
# write.table(plot.out2.bw,file="B73.v.W22_features.out.txt",sep="\t",quote=F,row.names = F)
# names(plot.out2.bp)[14] <- "RER"
# plot.out2.bp$imprint <- NULL
# write.table(plot.out2.bp,file="B73.v.PH207_features.out.txt",sep="\t",quote=F,row.names = F)
# names(plot.out2.wp)[14] <- "RER"
# plot.out2.wp$imprint <- NULL
# write.table(plot.out2.wp,file="W22.v.PH207_features.out.txt",sep="\t",quote=F,row.names = F)
LS0tCnRpdGxlOiAiSW1wcmludGluZyBHaXRodWIiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCmBgYHtyfQpsaWJyYXJ5KHRpZHlyKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkobWF0cml4U3RhdHMpCmxpYnJhcnkoREVTZXEyKQpsaWJyYXJ5KHN0cmluZ3IpCmxpYnJhcnkoZ2dFeHRyYSkKbGlicmFyeShncmlkRXh0cmEpCmxpYnJhcnkocGhlYXRtYXApCmxpYnJhcnkocmVzaGFwZTIpCmxpYnJhcnkoImdnYWxsdXZpYWwiKQpgYGAKCmBgYHtyfQpnaXZlLm4gPC0gZnVuY3Rpb24oeCl7CiAgIHJldHVybihjKHkgPSBtZWFuKHgpLCBsYWJlbCA9IGxlbmd0aCh4KSkpCn0KYGBgCgpTTlAtQVNFIG91dHB1dApTZWUgaHR0cHM6Ly9naXRodWIuY29tL29yaW9uemhvdS9ybmFzZXEvYmxvYi9tYXN0ZXIvb3V0cHV0Lm1kCmBgYHtyfQpyZHMgPC0gcmVhZFJEUygiYXNlXzFPY3QxOS5yZHMiKQphc2UucmVhZHMgPC0gcmRzCmNwbSA8LSByZWFkUkRTKCJjcG0ucmRzIikKbGliLnNpemUudGFiIDwtIGRhdGEuZnJhbWUoY3BtJHRsKQpgYGAKCmBgYHtyfQphc2UucmVhZHMkcmF0aW8gPC0gYXNlLnJlYWRzJGFsbGVsZTEvKGFzZS5yZWFkcyRhbGxlbGUxICsgYXNlLnJlYWRzJGFsbGVsZTIpCmFzZS5yZWFkcyR0b3RhbC5yZWFkcyA8LSAoYXNlLnJlYWRzJGFsbGVsZTEgKyBhc2UucmVhZHMkYWxsZWxlMikKYXNlLnJlYWRzJGNvbnRyYXN0IDwtIHN1YnN0cihhc2UucmVhZHMkc2lkLDAsMikKYXNlLnJlYWRzMiA8LSBkYXRhLmZyYW1lKGFzZS5yZWFkcyAlPiUgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyb3VwX2J5KGdpZCxjb250cmFzdCkgJT4lIAogICAgICAgICAgICAgICAgICAgICAgICAgICBkcGx5cjo6c3VtbWFyaXplKAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1lYW4ucmF0aW8gPSBtZWFuKHJhdGlvKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZWFuLmNvdW50ID0gbWVhbih0b3RhbC5yZWFkcykpKQpgYGAKCkdlbmUga2V5CkI3MyByZWZlcmVuY2UgZ2VuZSBtb2RlbCBjcm9zcy1yZWZlcmVuY2UgcmVsYXRpdmUgdG8gdjQKRG93bmxvYWRlZCBmcm9tIE1haXplR0RCIDIwMjAvMDEvMjIsIDExOjMyYW0KUmVmb3JtYXR0ZWQgdG8gcmVtb3ZlIGhlYWRlcgpgYGB7cn0KZ2VuZS5rZXkgPC0gcmVhZC50YWJsZSgiZ2VuZV9tb2RlbF94cmVmX3Y0Yy50eHQiLHNlcD0iXHQiLGhlYWRlcj1ULHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQpkdXAuZ2VuZSA8LSBzdWJzZXQoZ2VuZS5rZXksZHVwbGljYXRlZChnZW5lLmtleSR2NF9nZW5lX21vZGVsKSkKZ2VuZS5rZXkgPC0gc3Vic2V0KGdlbmUua2V5LCFnZW5lLmtleSR2NF9nZW5lX21vZGVsICVpbiUgZHVwLmdlbmUkdjRfZ2VuZV9tb2RlbCkKZ2VuZS5rZXkuQlcuc3ludGVsb2dzIDwtIHN1YnNldChnZW5lLmtleSxuY2hhcihnZW5lLmtleSRXMjIuWm0wMDAwNGIuMS4pID09IDE0KVssYygxOjQsOCwxMCwyMCwyNCldCmdlbmUua2V5LmJwLnN5bnRlbG9ncyA8LSBzdWJzZXQoZ2VuZS5rZXksbmNoYXIoZ2VuZS5rZXkkUEgyMDcuWm0wMDAwOGEuMS4pID09IDE0KVssYygxOjQsOCwxMCwyMCwyNCldCmdlbmUua2V5LkJXUCA8LSBzdWJzZXQoZ2VuZS5rZXksbmNoYXIoZ2VuZS5rZXkkVzIyLlptMDAwMDRiLjEuKSA9PSAxNCAmIG5jaGFyKGdlbmUua2V5JFBIMjA3LlptMDAwMDhhLjEuKSA9PSAxNClbLGMoMTo0LDgsMTAsMjAsMjQpXQpgYGAKClJlYWQgaW4gUkVSIHRhYmxlIGFuZCBjYWxjdWxhdGUgUlBNCmBgYHtyfQppbXAuYWxsIDwtIHJlYWQudGFibGUoImNvbWJpbmVkX2NvdW50c19hbGxfY29uY2F0aW5hdGVkX2dlbm9tZXNfMjFPY3QxOS50eHQiLGhlYWRlcj1ULHN0cmluZ3NBc0ZhY3RvcnMgPSBGKSAjIHVwZGF0ZSBwYXRoIAppbXAuYWxsJElEIDwtIHN0cl9yZXBsYWNlX2FsbChpbXAuYWxsJElELCIudjEuMSIsIiIpCmltcC5hbGwucnBtIDwtIGltcC5hbGwKZm9yKGkgaW4gMjoxOSl7CiAgaW1wLmFsbC5ycG1bLGldIDwtIGltcC5hbGxbLGldL2xpYi5zaXplLnRhYltsaWIuc2l6ZS50YWIkU2FtcGxlSUQgPT0gY29sbmFtZXMoaW1wLmFsbFtpXSksImxpYlNpemUiXSAqIDFlNgp9CgppbXAuYWxsMiA8LSBzdWJzZXQoaW1wLmFsbCwhaW1wLmFsbCRJRCAlaW4lIGMoIm5vX2ZlYXR1cmUiLCJhbWJpZ3VvdXMiLCJ0b29fbG93X2FRdWFsIiwibm90X2FsaWduZWQiLCJhbGlnbm1lbnRfbm90X3VuaXF1ZSIpKSAjIHJlbW92ZSBub24tZmVhdHVyZSBhbmQgYW1iaWd1b3VzIHJlYWRzCmltcC5hbGwyJElEW2dyZXBsKCJnZW5lIixpbXAuYWxsMiRJRCldIDwtIHN1YnN0cihpbXAuYWxsMiRJRFtncmVwbCgiZ2VuZSIsaW1wLmFsbDIkSUQpXSw2LDE5KSAjIGZpeCBmb3JtYXR0aW5nCmBgYAoKU3VtbWFyeSBzdGF0cyBhYm91dCBsaWJyYXJpZXMKYGBge3J9CmltcC5hbGwubnVtZXJhdG9yIDwtIHN1YnNldChpbXAuYWxsLCFpbXAuYWxsJElEICVpbiUgYygidG9vX2xvd19hUXVhbCIsIm5vdF9hbGlnbmVkIiwiYWxpZ25tZW50X25vdF91bmlxdWUiKSkgIyByZW1vdmUgbm9uLWZlYXR1cmUgYW5kIGFtYmlndW91cyByZWFkcwppbXAuYWxsLmRlbm9taW5hdG9yIDwtIHN1YnNldChpbXAuYWxsLCFpbXAuYWxsJElEICVpbiUgYygidG9vX2xvd19hUXVhbCIsIm5vdF9hbGlnbmVkIikpICMgcmVtb3ZlIG5vbi1mZWF0dXJlIGFuZCBhbWJpZ3VvdXMgcmVhZHMKCnN1bW1hcnkuc3RhdHMubGlicmFyaWVzIDwtIGRhdGEuZnJhbWUoY2JpbmQoY29sU3VtcyhpbXAuYWxsLmRlbm9taW5hdG9yWywyOjE5XSksKGNvbFN1bXMoaW1wLmFsbC5udW1lcmF0b3JbLDI6MTldKS9jb2xTdW1zKGltcC5hbGwuZGVub21pbmF0b3JbLDI6MTldKSkqMTAwKSkKbmFtZXMoc3VtbWFyeS5zdGF0cy5saWJyYXJpZXMpIDwtIGMoIlJlYWRzIiwiUGVyY2VudF9VbmlxdWUiKQptZWFuKHN1bW1hcnkuc3RhdHMubGlicmFyaWVzJFBlcmNlbnRfVW5pcXVlKQpgYGAKCgpgYGB7cn0KaW1wLmFsbC5ycG0yIDwtIHN1YnNldChpbXAuYWxsLnJwbSwhaW1wLmFsbC5ycG0kSUQgJWluJSBjKCJub19mZWF0dXJlIiwiYW1iaWd1b3VzIiwidG9vX2xvd19hUXVhbCIsIm5vdF9hbGlnbmVkIiwiYWxpZ25tZW50X25vdF91bmlxdWUiKSkgIyByZW1vdmUgbm9uLWZlYXR1cmUgYW5kIGFtYmlndW91cyByZWFkcwpyb3duYW1lcyhpbXAuYWxsLnJwbTIpIDwtIGltcC5hbGwucnBtMiRJRAppbXAuYWxsLnJwbTIkSUQgPC0gTlVMTApyb3duYW1lcyhpbXAuYWxsLnJwbTIpW2dyZXBsKCJnZW5lIixyb3duYW1lcyhpbXAuYWxsLnJwbTIpKV0gPC0gc3Vic3RyKHJvd25hbWVzKGltcC5hbGwucnBtMilbZ3JlcGwoImdlbmUiLHJvd25hbWVzKGltcC5hbGwucnBtMikpXSw2LDE5KSAjIGZpeCBmb3JtYXR0aW5nCmBgYAoKCmBgYHtyfQppbXAuYWxsLnJwbTIkQlcubWVhbiA8LSByb3dNZWFucyhpbXAuYWxsLnJwbTJbLDE6M10pICNpZ25vcmUgdGhpcyAoSSBoYXJkIGNvZGVkIGNvbHVtbiBudW1iZXJzIGxhdGVyKQppbXAuYWxsLnJwbTIkV0IubWVhbiA8LSByb3dNZWFucyhpbXAuYWxsLnJwbTJbLDQ6Nl0pICNpZ25vcmUgdGhpcyAoSSBoYXJkIGNvZGVkIGNvbHVtbiBudW1iZXJzIGxhdGVyKQppbXAuYWxsLnJwbTIkQl9yYXRpbyA8LSBpbXAuYWxsLnJwbTIkQlcubWVhbi8oaW1wLmFsbC5ycG0yJEJXLm1lYW4gKyBpbXAuYWxsLnJwbTIkV0IubWVhbikgI2lnbm9yZSB0aGlzIChJIGhhcmQgY29kZWQgY29sdW1uIG51bWJlcnMgbGF0ZXIpCgppbXAuYWxsLnJwbTIkZmVhdHVyZV90eXBlIDwtIGlmZWxzZShuY2hhcihyb3duYW1lcyhpbXAuYWxsLnJwbTIpKSA9PSAyMSwgIlRFIiwiZ2VuZSIpICMgTGFiZWwgZmVhdHVyZXMgYXMgVEVzIG9yIGdlbmVzCmltcC5hbGwucnBtMiRnZW5vbWUgPC0gaWZlbHNlKGdyZXBsKCJabTAwMDAxZCIscm93bmFtZXMoaW1wLmFsbC5ycG0yKSksIkI3MyIsaWZlbHNlKGdyZXBsKCJabTAwMDA0YiIscm93bmFtZXMoaW1wLmFsbC5ycG0yKSksIlcyMiIsIlBIMjA3IikpICMgTGFiZWwgd2hpY2ggZ2Vub21lIHRoZSBmZWF0dXJlIGFubm90YXRpb24gaXMgZnJvbQp0YWJsZShpbXAuYWxsLnJwbTIkZ2Vub21lKQpgYGAKCkZvciBlYWNoIHBhaXIgb2YgZ2Vub3R5cGVzLCBtYWtlIHRoZSBzYW1lIGNvbHVtbnMgYW5kIHRoZW4gZG8gdGhlIG1hdGggZm9yIHRoZSBtYXRlcm5hbCBwcmVmZXJlbmNlIHJhdGlvCmBgYHtyfQppbXAuQnZXLnJwbSA8LSBpbXAuYWxsLnJwbTJbLGMoMTo2LDIyOjIzKV0KaW1wLkJ2Vy5ycG0kY29udHJhc3QgPC0gIkJXIgppbXAuQnZXLnJwbSR0eXBlIDwtIGlmZWxzZShpbXAuQnZXLnJwbSRnZW5vbWUgPT0gIkI3MyIsIkEiLCJCIikKaW1wLkJ2Vy5ycG0kZmVhdHVyZSA8LSByb3duYW1lcyhpbXAuQnZXLnJwbSkKCmltcC5CdlAucnBtIDwtIGltcC5hbGwucnBtMlssYyg3OjEyLDIyOjIzKV0KaW1wLkJ2UC5ycG0kY29udHJhc3QgPC0gIkJQIgppbXAuQnZQLnJwbSR0eXBlIDwtIGlmZWxzZShpbXAuQnZXLnJwbSRnZW5vbWUgPT0gIkI3MyIsIkEiLCJCIikKaW1wLkJ2UC5ycG0kZmVhdHVyZSA8LSByb3duYW1lcyhpbXAuQnZQLnJwbSkKCmltcC5XdlAucnBtIDwtIGltcC5hbGwucnBtMlssYygxMzoxOCwyMjoyMyldCmltcC5XdlAucnBtJGNvbnRyYXN0IDwtICJXUCIKaW1wLld2UC5ycG0kdHlwZSA8LSBpZmVsc2UoaW1wLkJ2Vy5ycG0kZ2Vub21lID09ICJXMjIiLCJBIiwiQiIpCmltcC5XdlAucnBtJGZlYXR1cmUgPC0gcm93bmFtZXMoaW1wLld2UC5ycG0pCgpuYW1lcyhpbXAuQnZXLnJwbSkgPC0gbmFtZXMoaW1wLkJ2UC5ycG0pIDwtIG5hbWVzKGltcC5XdlAucnBtKSA8LSBjKCJBQjEiLCJBQjIiLCJBQjMiLCJCQTEiLCJCQTIiLCJCQTMiLCJmZWF0dXJlX3R5cGUiLCJnZW5vbWUiLCJjb250cmFzdCIsInR5cGUiLCJmZWF0dXJlIikKCmltcC5yZWNpcHMucnBtIDwtIGRhdGEuZnJhbWUocmJpbmQoaW1wLkJ2Vy5ycG0saW1wLkJ2UC5ycG0saW1wLld2UC5ycG0pKQoKaW1wLnJlY2lwcy5ycG0kQV9tZWFuIDwtIHJvd01lYW5zKGltcC5yZWNpcHMucnBtWywxOjNdKQppbXAucmVjaXBzLnJwbSRCX21lYW4gPC0gcm93TWVhbnMoaW1wLnJlY2lwcy5ycG1bLDQ6Nl0pCmltcC5yZWNpcHMucnBtJG1hdGVybmFsX3ByZWZlcmVuY2UgPC0gaWZlbHNlKGltcC5yZWNpcHMucnBtJHR5cGUgPT0gIkEiLGltcC5yZWNpcHMucnBtJEFfbWVhbi8oaW1wLnJlY2lwcy5ycG0kQV9tZWFuICsgaW1wLnJlY2lwcy5ycG0kQl9tZWFuKSxpbXAucmVjaXBzLnJwbSRCX21lYW4vKGltcC5yZWNpcHMucnBtJEFfbWVhbiArIGltcC5yZWNpcHMucnBtJEJfbWVhbikpICMgbWF0ZXJuYWwgcHJlZmVyYW5jZSBjYWxjdWxhdGlvbgpgYGAKClBsb3QgdGhlIG1hdGVybmFsIHByZWZlcmFuY2UgZGlzdHJpYnV0aXBucyBmb3IgZWFjaCBjb250cmFzdCwgZ2Vub21lLCBhbmQgZmVhdHVyZSB0eXBlCmBgYHtyfQppbXAucmVjaXBzLnJwbS5maWx0ZXIgPC0gc3Vic2V0KGltcC5yZWNpcHMucnBtLGltcC5yZWNpcHMucnBtJEFfbWVhbiA+PSAxIHwgaW1wLnJlY2lwcy5ycG0kQl9tZWFuID49IDEpCgpnZ3Bsb3QoaW1wLnJlY2lwcy5ycG0uZmlsdGVyLGFlcyh4PWdlbm9tZSx5PW1hdGVybmFsX3ByZWZlcmVuY2UsZmlsbD1nZW5vbWUpKSsgZ2VvbV9hYmxpbmUoc2xvcGU9MCwgaW50ZXJjZXB0PS4zMykgKyBnZW9tX2FibGluZShzbG9wZT0wLCBpbnRlcmNlcHQ9LjY2KSArIGdlb21fdmlvbGluKCkgKyB0aGVtZV9jbGFzc2ljKCkgKyB0aGVtZShwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCkpICsgZmFjZXRfd3JhcChmZWF0dXJlX3R5cGV+Y29udHJhc3Qsc2NhbGVzPSJmcmVlX3giKSAgKyBzdGF0X3N1bW1hcnkoZnVuLmRhdGEgPSBnaXZlLm4sIGdlb20gPSAidGV4dCIpIApgYGAKCkxvb2sgYXQgcGVyLWVsZW1lbnQgb3IgZ2VuZSBleHByZXNzaW9uIGFjcm9zcyBkZXZlbG9wbWVudApgYGB7cn0KZGV2LnJlYWRzIDwtIHJlYWQudGFibGUoImVsZW1lbnRfY29tYmluZWRfY291bnRzX0RldlJFRE9fMTFPY3QxOS50eHQiLGhlYWRlcj1ULHN0cmluZ3NBc0ZhY3RvcnMgPSBGLHNlcD0iXHQiKQpgYGAKCmBgYHtyfQpyb3duYW1lcyhkZXYucmVhZHMpIDwtIGRldi5yZWFkcyRlbGVtZW50CmRldi5yZWFkcyRlbGVtZW50IDwtIE5VTEwKZGV2LnJlYWRzMiA8LSBkZXYucmVhZHNbLGdyZXAoInRlLmciLGNvbG5hbWVzKGRldi5yZWFkcyksaW52ZXJ0ID0gVCldCm5hbWVzKGRldi5yZWFkczIpIDwtIHN1YnN0cihuYW1lcyhkZXYucmVhZHMyKSwwLG5jaGFyKG5hbWVzKGRldi5yZWFkczIpKS03KQpkZXYucmVhZHMzIDwtIGRldi5yZWFkczJbLGdyZXAoIkRTIixjb2xuYW1lcyhkZXYucmVhZHMyKSldCgogIApkZXYucnBtIDwtIGRldi5yZWFkczMKZm9yKGkgaW4gMTpuY29sKGRldi5ycG0pKXsKICBkZXYucnBtWyxpXSA8LSBkZXYucmVhZHMzWyxpXS9zdW0oZGV2LnJlYWRzM1ssaV0pICogMWU2Cn0KYGBgCgoKYGBge3J9CmFsbC5zYW1wbGVzIDwtIGRhdGEuZnJhbWUobmFtZXMoZGV2LnJwbSkpCmFsbC5zYW1wbGVzJGNvbG5hbWUgPC0gcGFzdGUobmFtZXMoZGV2LnJwbSkpCmFsbC5zYW1wbGVzIDwtIHNlcGFyYXRlKGFsbC5zYW1wbGVzLG5hbWVzLmRldi5ycG0uLGMoInRpc3N1ZSIsImRldGFpbHMiLCJleHBlcmltZW50IiwiZ2Vub3R5cGUiLCJyZXAiKSxzZXA9Il8iKQpgYGAKCk1ha2UgZmlsdGVyIGZvciBwb3RlbnRpYWwgbWF0ZXJuYWwgY29udGFtaW5hdGlvbiBNRUdzIChURSBwbHVzIGdlbmUpCmBgYHtyfQprZWVwLmNvbHVtbnMgPC0gYygiZW5kb3NwZXJtXzE0ZGFwX0RTX0JfMSIsImVuZG9zcGVybV8xNGRhcF9EU19CXzIiLCJlbmRvc3Blcm1fMTRkYXBfRFNfQl8zIiwic2VlZF9wZXJpY2FycC4xOGRhcF9EU19CXzEiLCJzZWVkX3BlcmljYXJwLjE4ZGFwX0RTX0JfMiIsInNlZWRfcGVyaWNhcnAuMThkYXBfRFNfQl8zIikKQjczLmVuZG9wZXJpLnN0ZXBmbHVnIDwtIHN1YnNldChkZXYucnBtLCByb3dNYXhzKGFzLm1hdHJpeChkZXYucnBtWyxrZWVwLmNvbHVtbnNdKSkgPiAwKVssa2VlcC5jb2x1bW5zXQpCNzMuZW5kb3Blcmkuc3RlcGZsdWckbWVhbi5lbmRvICA8LSByb3dNZWFucyhCNzMuZW5kb3Blcmkuc3RlcGZsdWdbLDE6M10pCkI3My5lbmRvcGVyaS5zdGVwZmx1ZyRtZWFuLnBlcmkgPC0gcm93TWVhbnMoQjczLmVuZG9wZXJpLnN0ZXBmbHVnWyw0OjZdKQpwZXJpLnByZWZlcnJlZCA8LSBzdWJzZXQoQjczLmVuZG9wZXJpLnN0ZXBmbHVnLEI3My5lbmRvcGVyaS5zdGVwZmx1ZyRtZWFuLnBlcmkgPiAyICogQjczLmVuZG9wZXJpLnN0ZXBmbHVnJG1lYW4uZW5kbykKbnJvdyhwZXJpLnByZWZlcnJlZCkKdGFibGUobmNoYXIocm93bmFtZXMocGVyaS5wcmVmZXJyZWQpKSkKcGVyaS5wcmVmZXJyZWQuVzIyIDwtIHN1YnNldChnZW5lLmtleSxnZW5lLmtleSR2NF9nZW5lX21vZGVsICVpbiUgcm93bmFtZXMocGVyaS5wcmVmZXJyZWQpKVssIlcyMi5abTAwMDA0Yi4xLiJdCnBlcmkucHJlZmVycmVkLlBIMjA3IDwtIHN1YnNldChnZW5lLmtleSxnZW5lLmtleSR2NF9nZW5lX21vZGVsICVpbiUgcm93bmFtZXMocGVyaS5wcmVmZXJyZWQpKVssIlBIMjA3LlptMDAwMDhhLjEuIl0KYGBgCgpVc2UgREVzZXEgdG8gY2FsbCBzaWduaWZpY2FuY2Ugb3ZlciBhIGxvZzJGQyBjdXRvZmYgb2YgMSAoMi1mb2xkLCB3aGljaCBpcyB0aGUgZXhwZWN0ZWQgcmF0aW8pCmBgYHtyfQppbXAuYWxsLmRlLmJ3IDwtIGltcC5hbGwyWywyOjddCnJvd25hbWVzKGltcC5hbGwuZGUuYncpIDwtIGltcC5hbGwyJElECiNpbXAuYWxsLmRlLmtlZXAuYncgPC0gc3Vic2V0KGltcC5hbGwuZGUuYncscm93U3VtcyhpbXAuYWxsLmRlLmJ3ID4wKSA+PSAzKSAKaW1wLmFsbC5kZS5rZWVwLmJ3IDwtIHN1YnNldChpbXAuYWxsLmRlLmJ3LHJvd1N1bXMoaW1wLmFsbC5kZS5idykgPj0gMTApIAojaW1wLmFsbC5kZS5rZWVwLmJ3IDwtIHN1YnNldChpbXAuYWxsLmRlLmJ3LHJvd01lYW5zKGltcC5hbGwucnBtMlssMTozXSkgPj0gMC41IHwgcm93TWVhbnMoaW1wLmFsbC5ycG0yWyw0OjZdKSA+PSAwLjUpIAoKc2FtcGxlLmluZm8uYncgPC0gYXMuZGF0YS5mcmFtZShjYmluZChwYXN0ZShuYW1lcyhpbXAuYWxsLmRlLmtlZXAuYncpKSxjKCJCVyIsIkJXIiwiQlciLCJXQiIsIldCIiwiV0IiKSkpCnJvd25hbWVzKHNhbXBsZS5pbmZvLmJ3KSA8LSBzYW1wbGUuaW5mby5id1ssMV0Kc2FtcGxlX2xpc3QuYncgPC0gc2FtcGxlLmluZm8uYndbLDJdCm5hbWVzKHNhbXBsZS5pbmZvLmJ3KSA8LSBjKCJzYW1wbGUuaW5mby5idyIsInNhbXBsZV9saXN0IikKCmRkcy5idyA8LSBERVNlcURhdGFTZXRGcm9tTWF0cml4KGNvdW50RGF0YSA9IGltcC5hbGwuZGUua2VlcC5idyxjb2xEYXRhID0gc2FtcGxlLmluZm8uYncsIGRlc2lnbiA9IH4gc2FtcGxlX2xpc3QpCmRkcy5idyA8LSBERVNlcShkZHMuYncpCnJlcy5idyA8LSByZXN1bHRzKGRkcy5idywgbGZjVGhyZXNob2xkPTEsIGFsdEh5cG90aGVzaXMgPSAiZ3JlYXRlckFicyIpCmRyYXdMaW5lcyA8LSBmdW5jdGlvbigpIGFibGluZShoPWMoLTEsMSksY29sPSJkb2RnZXJibHVlIixsd2Q9MikKI3BhcihtZnJvdz1jKDIsMiksbWFyPWMoMiwyLDEsMSkpCnlsaW0gPC0gYygtMi41LDIuNSkKcGxvdE1BKHJlcy5idywgeWxpbT15bGltKTsgZHJhd0xpbmVzKCkKYGBgCgpQbG90IHJlc3VsdHMgd2l0aCByZXNwZWN0IHRvIFJFUgpgYGB7cn0Kb3V0LmJ3IDwtIGRhdGEuZnJhbWUocmVzLmJ3KQpwbG90Lm91dC5idyA8LSBtZXJnZShpbXAucmVjaXBzLnJwbSxvdXQuYncsYnkueD0iZmVhdHVyZSIsYnkueT0icm93Lm5hbWVzIixhbGw9RikKcGxvdC5vdXQuYnckc2lnIDwtIGlmZWxzZShwbG90Lm91dC5idyRwYWRqIDwgMC4wNSwgIlRSVUUiLCAiRkFMU0UiKQpwbG90Lm91dDIuYncgPC0gc3Vic2V0KHBsb3Qub3V0LmJ3LCFpcy5uYShwbG90Lm91dC5idyRzaWcpICYgcGxvdC5vdXQuYnckY29udHJhc3QgJWluJSBjKCJCVyIsIldCIikpCnBsb3Qub3V0Mi5idyRjYXRlZ29yeSA8LSBpZmVsc2UocGxvdC5vdXQyLmJ3JHBhZGogPCAwLjA1ICYgcGxvdC5vdXQyLmJ3JGxvZzJGb2xkQ2hhbmdlIDwgLTEgJiAocGxvdC5vdXQyLmJ3JG1hdGVybmFsX3ByZWZlcmVuY2UgPiAuOSB8IHBsb3Qub3V0Mi5idyRtYXRlcm5hbF9wcmVmZXJlbmNlIDwgLjEpLCAidXAiLGlmZWxzZShwbG90Lm91dDIuYnckcGFkaiA8IDAuMDUgJiBwbG90Lm91dDIuYnckbG9nMkZvbGRDaGFuZ2UgPiAxICYgKHBsb3Qub3V0Mi5idyRtYXRlcm5hbF9wcmVmZXJlbmNlID4gLjkgfCBwbG90Lm91dDIuYnckbWF0ZXJuYWxfcHJlZmVyZW5jZSA8IC4xKSwiZG93biIsIm5vdERFIikpCgpwbG90Lm91dDIuYnckaW1wcmludCA8LSBpZmVsc2UocGxvdC5vdXQyLmJ3JGNhdGVnb3J5ID09InVwIiAmIHBsb3Qub3V0Mi5idyRnZW5vbWUgPT0gIkI3MyIsIk1FRyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UocGxvdC5vdXQyLmJ3JGNhdGVnb3J5ID09ImRvd24iICYgcGxvdC5vdXQyLmJ3JGdlbm9tZSA9PSAiQjczIiwiUEVHIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UocGxvdC5vdXQyLmJ3JGNhdGVnb3J5ID09ImRvd24iICYgcGxvdC5vdXQyLmJ3JGdlbm9tZSA9PSAiVzIyIiwiTUVHIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKHBsb3Qub3V0Mi5idyRjYXRlZ29yeSA9PSJ1cCIgJiBwbG90Lm91dDIuYnckZ2Vub21lID09ICJXMjIiLCJQRUciLCJuby5pbXByaW50IikpKSkKCmdncGxvdChwbG90Lm91dDIuYncsYWVzKHg9aW1wcmludCx5PW1hdGVybmFsX3ByZWZlcmVuY2UsZmlsbD1pbXByaW50KSkgKyBnZW9tX3Zpb2xpbigpICsgdGhlbWVfY2xhc3NpYygpICsgdGhlbWUocGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSxwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpKSArIGZhY2V0X2dyaWQoZ2Vub21lfmZlYXR1cmVfdHlwZSkgKyBnZW9tX2FibGluZShzbG9wZT0wLCBpbnRlcmNlcHQ9LjMzKSArIGdlb21fYWJsaW5lKHNsb3BlPTAsIGludGVyY2VwdD0uNjYpICsgc3RhdF9zdW1tYXJ5KGZ1bi5kYXRhID0gZ2l2ZS5uLCBnZW9tID0gInRleHQiKSAKCnBsb3Qub3V0Mi5idyRmaWx0ZXIuaW1wcmludCA8LSBpZmVsc2UocGxvdC5vdXQyLmJ3JGltcHJpbnQgPT0gIk1FRyIgJiAocGxvdC5vdXQyLmJ3JGZlYXR1cmUgJWluJSByb3duYW1lcyhwZXJpLnByZWZlcnJlZCkgfCBwbG90Lm91dDIuYnckZmVhdHVyZSAlaW4lIHBlcmkucHJlZmVycmVkLlcyMiksImZpbHRlcmVkLk1FRyIscGxvdC5vdXQyLmJ3JGltcHJpbnQpCmBgYAoKCgpSZXBlYXQgZm9yIEJQCmBgYHtyfQppbXAuYWxsLmRlLmJwIDwtIGltcC5hbGwyWyw4OjEzXQpyb3duYW1lcyhpbXAuYWxsLmRlLmJwKSA8LSBpbXAuYWxsMiRJRAojaW1wLmFsbC5kZS5rZWVwLmJwIDwtIHN1YnNldChpbXAuYWxsLmRlLmJwLHJvd1N1bXMoaW1wLmFsbC5kZS5icCA+MCkgPj0gMykKaW1wLmFsbC5kZS5rZWVwLmJwIDwtIHN1YnNldChpbXAuYWxsLmRlLmJwLHJvd1N1bXMoaW1wLmFsbC5kZS5icCkgPj0gMTApCiNpbXAuYWxsLmRlLmtlZXAuYnAgPC0gc3Vic2V0KGltcC5hbGwuZGUuYnAscm93TWVhbnMoaW1wLmFsbC5ycG0yWyw3OjldKSA+PSAwLjUgfCByb3dNZWFucyhpbXAuYWxsLnJwbTJbLDEwOjEyXSkgPj0gMC41KSAKCnNhbXBsZS5pbmZvLmJwIDwtIGFzLmRhdGEuZnJhbWUoY2JpbmQocGFzdGUobmFtZXMoaW1wLmFsbC5kZS5rZWVwLmJwKSksYygiQlAiLCJCUCIsIkJQIiwiUEIiLCJQQiIsIlBCIikpKQpyb3duYW1lcyhzYW1wbGUuaW5mby5icCkgPC0gc2FtcGxlLmluZm8uYnBbLDFdCnNhbXBsZV9saXN0LmJwIDwtIHNhbXBsZS5pbmZvLmJwWywyXQpuYW1lcyhzYW1wbGUuaW5mby5icCkgPC0gYygic2FtcGxlLmluZm8uYnAiLCJzYW1wbGVfbGlzdCIpCgpkZHMuYnAgPC0gREVTZXFEYXRhU2V0RnJvbU1hdHJpeChjb3VudERhdGEgPSBpbXAuYWxsLmRlLmtlZXAuYnAsY29sRGF0YSA9IHNhbXBsZS5pbmZvLmJwLCBkZXNpZ24gPSB+IHNhbXBsZV9saXN0KQpkZHMuYnAgPC0gREVTZXEoZGRzLmJwKQpyZXMuYnAgPC0gcmVzdWx0cyhkZHMuYnAsIGxmY1RocmVzaG9sZD0xLCBhbHRIeXBvdGhlc2lzID0gImdyZWF0ZXJBYnMiKQojcGFyKG1mcm93PWMoMiwyKSxtYXI9YygyLDIsMSwxKSkKeWxpbSA8LSBjKC0yLjUsMi41KQpwbG90TUEocmVzLmJwLCB5bGltPXlsaW0pOyBkcmF3TGluZXMoKQpgYGAKClBsb3QgcmVzdWx0cyB3aXRoIHJlc3BlY3QgdG8gUkVSCmBgYHtyfQpvdXQuYnAgPC0gZGF0YS5mcmFtZShyZXMuYnApCnBsb3Qub3V0LmJwIDwtIG1lcmdlKGltcC5yZWNpcHMucnBtLG91dC5icCxieS54PSJmZWF0dXJlIixieS55PSJyb3cubmFtZXMiLGFsbD1GKQpwbG90Lm91dC5icCRzaWcgPC0gaWZlbHNlKHBsb3Qub3V0LmJwJHBhZGogPCAwLjA1LCAiVFJVRSIsICJGQUxTRSIpCnBsb3Qub3V0Mi5icCA8LSBzdWJzZXQocGxvdC5vdXQuYnAsIWlzLm5hKHBsb3Qub3V0LmJwJHNpZykgJiBwbG90Lm91dC5icCRjb250cmFzdCAlaW4lIGMoIkJQIiwiUEIiKSkKCnBsb3Qub3V0Mi5icCRjYXRlZ29yeSA8LSBpZmVsc2UocGxvdC5vdXQyLmJwJHBhZGogPCAwLjA1ICYgcGxvdC5vdXQyLmJwJGxvZzJGb2xkQ2hhbmdlIDwgLTEgJiAocGxvdC5vdXQyLmJwJG1hdGVybmFsX3ByZWZlcmVuY2UgPiAuOSB8IHBsb3Qub3V0Mi5icCRtYXRlcm5hbF9wcmVmZXJlbmNlIDwgLjEpLCAidXAiLGlmZWxzZShwbG90Lm91dDIuYnAkcGFkaiA8IDAuMDUgJiBwbG90Lm91dDIuYnAkbG9nMkZvbGRDaGFuZ2UgPiAxICYgKHBsb3Qub3V0Mi5icCRtYXRlcm5hbF9wcmVmZXJlbmNlID4gLjkgfCBwbG90Lm91dDIuYnAkbWF0ZXJuYWxfcHJlZmVyZW5jZSA8IC4xKSwiZG93biIsIm5vdERFIikpCgpwbG90Lm91dDIuYnAkaW1wcmludCA8LSBpZmVsc2UocGxvdC5vdXQyLmJwJGNhdGVnb3J5ID09InVwIiAmIHBsb3Qub3V0Mi5icCRnZW5vbWUgPT0gIkI3MyIsIk1FRyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UocGxvdC5vdXQyLmJwJGNhdGVnb3J5ID09ImRvd24iICYgcGxvdC5vdXQyLmJwJGdlbm9tZSA9PSAiQjczIiwiUEVHIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UocGxvdC5vdXQyLmJwJGNhdGVnb3J5ID09ImRvd24iICYgcGxvdC5vdXQyLmJwJGdlbm9tZSA9PSAiUEgyMDciLCJNRUciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UocGxvdC5vdXQyLmJwJGNhdGVnb3J5ID09InVwIiAmIHBsb3Qub3V0Mi5icCRnZW5vbWUgPT0gIlBIMjA3IiwiUEVHIiwibm8uaW1wcmludCIpKSkpCgpnZ3Bsb3QocGxvdC5vdXQyLmJwLGFlcyh4PWltcHJpbnQseT1tYXRlcm5hbF9wcmVmZXJlbmNlLGZpbGw9aW1wcmludCkpICsgZ2VvbV92aW9saW4oKSArIHRoZW1lX2NsYXNzaWMoKSArIHRoZW1lKHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCkscGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSkgKyBmYWNldF9ncmlkKGdlbm9tZX5mZWF0dXJlX3R5cGUpICsgZ2VvbV9hYmxpbmUoc2xvcGU9MCwgaW50ZXJjZXB0PS4zMykgKyBnZW9tX2FibGluZShzbG9wZT0wLCBpbnRlcmNlcHQ9LjY2KSArIHN0YXRfc3VtbWFyeShmdW4uZGF0YSA9IGdpdmUubiwgZ2VvbSA9ICJ0ZXh0IikgCgpwbG90Lm91dDIuYnAkZmlsdGVyLmltcHJpbnQgPC0gaWZlbHNlKHBsb3Qub3V0Mi5icCRpbXByaW50ID09ICJNRUciICYgKHBsb3Qub3V0Mi5icCRmZWF0dXJlICVpbiUgcm93bmFtZXMocGVyaS5wcmVmZXJyZWQpIHwgcGxvdC5vdXQyLmJwJGZlYXR1cmUgJWluJSBwZXJpLnByZWZlcnJlZC5QSDIwNyksImZpbHRlcmVkLk1FRyIscGxvdC5vdXQyLmJwJGltcHJpbnQpCmBgYAoKUmVwZWF0IGZvciBXUApgYGB7cn0KaW1wLmFsbC5kZS53cCA8LSBpbXAuYWxsMlssMTQ6MTldCnJvd25hbWVzKGltcC5hbGwuZGUud3ApIDwtIGltcC5hbGwyJElECiNpbXAuYWxsLmRlLmtlZXAud3AgPC0gc3Vic2V0KGltcC5hbGwuZGUud3Ascm93U3VtcyhpbXAuYWxsLmRlLndwID4wKSA+PSAzKQppbXAuYWxsLmRlLmtlZXAud3AgPC0gc3Vic2V0KGltcC5hbGwuZGUud3Ascm93U3VtcyhpbXAuYWxsLmRlLndwKSA+PSAxMCkKI2ltcC5hbGwuZGUua2VlcC53cCA8LSBzdWJzZXQoaW1wLmFsbC5kZS53cCxyb3dNZWFucyhpbXAuYWxsLnJwbTJbLDEzOjE1XSkgPj0gMC41IHwgcm93TWVhbnMoaW1wLmFsbC5ycG0yWywxNjoxOF0pID49IDAuNSkgCgpzYW1wbGUuaW5mby53cCA8LSBhcy5kYXRhLmZyYW1lKGNiaW5kKHBhc3RlKG5hbWVzKGltcC5hbGwuZGUua2VlcC53cCkpLGMoIldQIiwiV1AiLCJXUCIsIlBXIiwiUFciLCJQVyIpKSkKcm93bmFtZXMoc2FtcGxlLmluZm8ud3ApIDwtIHNhbXBsZS5pbmZvLndwWywxXQpzYW1wbGVfbGlzdC53cCA8LSBzYW1wbGUuaW5mby53cFssMl0KbmFtZXMoc2FtcGxlLmluZm8ud3ApIDwtIGMoInNhbXBsZS5pbmZvLndwIiwic2FtcGxlX2xpc3QiKQoKZGRzLndwIDwtIERFU2VxRGF0YVNldEZyb21NYXRyaXgoY291bnREYXRhID0gaW1wLmFsbC5kZS5rZWVwLndwLGNvbERhdGEgPSBzYW1wbGUuaW5mby53cCwgZGVzaWduID0gfiBzYW1wbGVfbGlzdCkKZGRzLndwIDwtIERFU2VxKGRkcy53cCkKcmVzLndwIDwtIHJlc3VsdHMoZGRzLndwLCBsZmNUaHJlc2hvbGQ9MSwgYWx0SHlwb3RoZXNpcyA9ICJncmVhdGVyQWJzIikKZHJhd0xpbmVzIDwtIGZ1bmN0aW9uKCkgYWJsaW5lKGg9YygtMSwxKSxjb2w9ImRvZGdlcmJsdWUiLGx3ZD0yKQojcGFyKG1mcm93PWMoMiwyKSxtYXI9YygyLDIsMSwxKSkKeWxpbSA8LSBjKC0yLjUsMi41KQpwbG90TUEocmVzLndwLCB5bGltPXlsaW0pOyBkcmF3TGluZXMoKQpgYGAKClBsb3QgcmVzdWx0cyB3aXRoIHJlc3BlY3QgdG8gdGhlIFJFUgpgYGB7cn0Kb3V0LndwIDwtIGRhdGEuZnJhbWUocmVzLndwKQpwbG90Lm91dC53cCA8LSBtZXJnZShpbXAucmVjaXBzLnJwbSxvdXQud3AsYnkueD0iZmVhdHVyZSIsYnkueT0icm93Lm5hbWVzIixhbGw9RikKcGxvdC5vdXQud3Akc2lnIDwtIGlmZWxzZShwbG90Lm91dC53cCRwYWRqIDwgMC4wNSwgIlRSVUUiLCAiRkFMU0UiKQpwbG90Lm91dDIud3AgPC0gc3Vic2V0KHBsb3Qub3V0LndwLCFpcy5uYShwbG90Lm91dC53cCRzaWcpICYgcGxvdC5vdXQud3AkY29udHJhc3QgJWluJSBjKCJXUCIsIlBXIikpCnBsb3Qub3V0Mi53cCRjYXRlZ29yeSA8LSBpZmVsc2UocGxvdC5vdXQyLndwJHBhZGogPCAwLjA1ICYgcGxvdC5vdXQyLndwJGxvZzJGb2xkQ2hhbmdlIDwgLTEgJiAocGxvdC5vdXQyLndwJG1hdGVybmFsX3ByZWZlcmVuY2UgPiAuOSB8IHBsb3Qub3V0Mi53cCRtYXRlcm5hbF9wcmVmZXJlbmNlIDwgLjEpLCAidXAiLGlmZWxzZShwbG90Lm91dDIud3AkcGFkaiA8IDAuMDUgJiBwbG90Lm91dDIud3AkbG9nMkZvbGRDaGFuZ2UgPiAxICYgKHBsb3Qub3V0Mi53cCRtYXRlcm5hbF9wcmVmZXJlbmNlID4gLjkgfCBwbG90Lm91dDIud3AkbWF0ZXJuYWxfcHJlZmVyZW5jZSA8IC4xKSwiZG93biIsIm5vdERFIikpCgpwbG90Lm91dDIud3AkaW1wcmludCA8LSBpZmVsc2UocGxvdC5vdXQyLndwJGNhdGVnb3J5ID09InVwIiAmIHBsb3Qub3V0Mi53cCRnZW5vbWUgPT0gIlBIMjA3IiwiTUVHIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShwbG90Lm91dDIud3AkY2F0ZWdvcnkgPT0iZG93biIgJiBwbG90Lm91dDIud3AkZ2Vub21lID09ICJQSDIwNyIsIlBFRyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKHBsb3Qub3V0Mi53cCRjYXRlZ29yeSA9PSJkb3duIiAmIHBsb3Qub3V0Mi53cCRnZW5vbWUgPT0gIlcyMiIsIk1FRyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShwbG90Lm91dDIud3AkY2F0ZWdvcnkgPT0idXAiICYgcGxvdC5vdXQyLndwJGdlbm9tZSA9PSAiVzIyIiwiUEVHIiwibm8uaW1wcmludCIpKSkpCgoKZ2dwbG90KHBsb3Qub3V0Mi53cCxhZXMoeD1pbXByaW50LHk9bWF0ZXJuYWxfcHJlZmVyZW5jZSxmaWxsPWltcHJpbnQpKSArIGdlb21fdmlvbGluKCkgKyB0aGVtZV9jbGFzc2ljKCkgKyB0aGVtZShwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCkpICsgZmFjZXRfZ3JpZChnZW5vbWV+ZmVhdHVyZV90eXBlKSArIGdlb21fYWJsaW5lKHNsb3BlPTAsIGludGVyY2VwdD0uMzMpICsgZ2VvbV9hYmxpbmUoc2xvcGU9MCwgaW50ZXJjZXB0PS42NikgKyBzdGF0X3N1bW1hcnkoZnVuLmRhdGEgPSBnaXZlLm4sIGdlb20gPSAidGV4dCIpIAoKcGxvdC5vdXQyLndwJGZpbHRlci5pbXByaW50IDwtIGlmZWxzZShwbG90Lm91dDIud3AkaW1wcmludCA9PSAiTUVHIiAmIChwbG90Lm91dDIud3AkZmVhdHVyZSAlaW4lIHBlcmkucHJlZmVycmVkLlcyMiB8IHBsb3Qub3V0Mi53cCRmZWF0dXJlICVpbiUgcGVyaS5wcmVmZXJyZWQuUEgyMDcpLCJmaWx0ZXJlZC5NRUciLHBsb3Qub3V0Mi53cCRpbXByaW50KQpgYGAKClN1bW1hcml6ZSBpbXByaW50aW5nIGNhbGxzCmBgYHtyfQpzdW1tYXJ5LmJ3IDwtIGRhdGEuZnJhbWUocGxvdC5vdXQyLmJ3ICU+JSBncm91cF9ieShnZW5vbWUsZmVhdHVyZV90eXBlLGZpbHRlci5pbXByaW50LGNvbnRyYXN0KSAlPiUgc3VtbWFyaXplKGNvdW50ID0gbigpKSkKc3VtbWFyeS5icCA8LSBkYXRhLmZyYW1lKHBsb3Qub3V0Mi5icCAlPiUgZ3JvdXBfYnkoZ2Vub21lLGZlYXR1cmVfdHlwZSxmaWx0ZXIuaW1wcmludCxjb250cmFzdCkgJT4lIHN1bW1hcml6ZShjb3VudCA9IG4oKSkpCnN1bW1hcnkud3AgPC0gZGF0YS5mcmFtZShwbG90Lm91dDIud3AgJT4lIGdyb3VwX2J5KGdlbm9tZSxmZWF0dXJlX3R5cGUsZmlsdGVyLmltcHJpbnQsY29udHJhc3QpICU+JSBzdW1tYXJpemUoY291bnQgPSBuKCkpKQoKc3VtbWFyeS5jb21iaW5lZCA8LSByYmluZChzdW1tYXJ5LmJ3LHN1bW1hcnkuYnAsc3VtbWFyeS53cCkKc3VtbWFyeS5jb21iaW5lZAoKc3VtbWFyeS5jb21iaW5lZDIgPC0gc3Vic2V0KHN1bW1hcnkuY29tYmluZWQsc3VtbWFyeS5jb21iaW5lZCRmaWx0ZXIuaW1wcmludCAlaW4lIGMoIk1FRyIsIlBFRyIpKQpzdW1tYXJ5LmNvbWJpbmVkMltzdW1tYXJ5LmNvbWJpbmVkMiRmaWx0ZXIuaW1wcmludCA9PSAiUEVHIiwiY291bnQiXSA8LSAtc3VtbWFyeS5jb21iaW5lZDJbc3VtbWFyeS5jb21iaW5lZDIkZmlsdGVyLmltcHJpbnQgPT0gIlBFRyIsImNvdW50Il0Kc3VtbWFyeS5jb21iaW5lZDIkbGFiZWwgPC0gcGFzdGUoc3VtbWFyeS5jb21iaW5lZDIkZ2Vub21lLHN1bW1hcnkuY29tYmluZWQyJGNvbnRyYXN0LHNlcD0iXyIpCgpnZ3Bsb3QoKSArIGdlb21fYmFyKGFlcyh4PWxhYmVsLHk9Y291bnQsZmlsbD1maWx0ZXIuaW1wcmludCksZGF0YT1zdW1tYXJ5LmNvbWJpbmVkMixzdGF0PSJpZGVudGl0eSIscG9zaXRpb24gPSAiaWRlbnRpdHkiKSArIHRoZW1lX2NsYXNzaWMoKSAgKyAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGU9OTAsIGhqdXN0PTEpKSArIGZhY2V0X2dyaWQoLn5mZWF0dXJlX3R5cGUsc2NhbGVzPSJmcmVlIikgKyBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9YygibWFnZW50YTIiLCJuYXZ5IikpIApgYGAKQXZlcmFnZSBpbXByaW50ZWQgZ2VuZXMKYGBge3J9CmltcC5nZW5lcyA8LSBzdWJzZXQoc3VtbWFyeS5jb21iaW5lZDIsc3VtbWFyeS5jb21iaW5lZDIkZmVhdHVyZV90eXBlID09ICJnZW5lIikKaW1wLmdlbmVzW2ltcC5nZW5lcyRmaWx0ZXIuaW1wcmludCA9PSAiUEVHIiwiY291bnQiXSA8LSAtaW1wLmdlbmVzW2ltcC5nZW5lcyRmaWx0ZXIuaW1wcmludCA9PSAiUEVHIiwiY291bnQiXQoKaW1wLmdlbmVzMiA8LSBpbXAuZ2VuZXMlPiUgZ3JvdXBfYnkobGFiZWwpICU+JSBkcGx5cjo6c3VtbWFyaXNlKHRvdGFsLmltcHJpbnQgPSBzdW0oY291bnQpKQptZWFuKGltcC5nZW5lczIkdG90YWwuaW1wcmludCkKCm1lYW4oc3Vic2V0KGltcC5nZW5lcyxpbXAuZ2VuZXMkZmlsdGVyLmltcHJpbnQgPT0gIk1FRyIpWywiY291bnQiXSkKCm1lYW4oc3Vic2V0KGltcC5nZW5lcyxpbXAuZ2VuZXMkZmlsdGVyLmltcHJpbnQgPT0gIlBFRyIpWywiY291bnQiXSkKYGBgCgpgYGB7cn0KbWVhbihzdWJzZXQoc3VtbWFyeS5jb21iaW5lZDIsc3VtbWFyeS5jb21iaW5lZDIkZmVhdHVyZV90eXBlID09ICJURSIgJiBzdW1tYXJ5LmNvbWJpbmVkMiRmaWx0ZXIuaW1wcmludCA9PSAiTUVHIilbLCJjb3VudCJdKQpgYGAKCgoKClVzZSB0byBtYWtlIHZlbm5zIC0gQjczIHZzIFcyMgpgYGB7cn0KY29tcGFyZS5idy5zeW50ZWxvZ3MgPC0gc3Vic2V0KHBsb3Qub3V0Mi5idyxwbG90Lm91dDIuYnckZmVhdHVyZV90eXBlID09ICJnZW5lIikKY29tcGFyZS5idy5zeW50ZWxvZ3Mkc3ludGVsb2cgPC0gaWZlbHNlKGNvbXBhcmUuYncuc3ludGVsb2dzJGZlYXR1cmUgJWluJSBnZW5lLmtleS5CVy5zeW50ZWxvZ3MkdjRfZ2VuZV9tb2RlbCB8IGNvbXBhcmUuYncuc3ludGVsb2dzJGZlYXR1cmUgJWluJSBnZW5lLmtleS5CVy5zeW50ZWxvZ3MkVzIyLlptMDAwMDRiLjEuLCAic3ludGVsb2ciLCJub24uc3ludGVsb2ciKQoKdGFibGUoY29tcGFyZS5idy5zeW50ZWxvZ3NbLGMoImdlbm9tZSIsImZpbHRlci5pbXByaW50Iiwic3ludGVsb2ciKV0pCgpjb21wYXJlLmJ3LnN5bnRlbG9ncy5iNzMgPC0gbWVyZ2Uoc3Vic2V0KGNvbXBhcmUuYncuc3ludGVsb2dzLGNvbXBhcmUuYncuc3ludGVsb2dzJHN5bnRlbG9nID09ICJzeW50ZWxvZyIgJiBjb21wYXJlLmJ3LnN5bnRlbG9ncyRnZW5vbWUgPT0gIkI3MyIpLGdlbmUua2V5LkJXLnN5bnRlbG9ncyxieS54PSJmZWF0dXJlIixieS55PSJ2NF9nZW5lX21vZGVsIixhbGwueD1UKQpjb21wYXJlLmJ3LnN5bnRlbG9ncy53MjIgPC0gc3Vic2V0KGNvbXBhcmUuYncuc3ludGVsb2dzLGNvbXBhcmUuYncuc3ludGVsb2dzJHN5bnRlbG9nID09ICJzeW50ZWxvZyIgJiBjb21wYXJlLmJ3LnN5bnRlbG9ncyRnZW5vbWUgPT0gIlcyMiIpCmNvbXBhcmUuYncuc3ludGVsb2dzLm1lcmdlIDwtIG1lcmdlKGNvbXBhcmUuYncuc3ludGVsb2dzLmI3Myxjb21wYXJlLmJ3LnN5bnRlbG9ncy53MjIsYnkueCA9ICJXMjIuWm0wMDAwNGIuMS4iLGJ5Lnk9ImZlYXR1cmUiLGFsbD1UKQoKbmFtZXMoY29tcGFyZS5idy5zeW50ZWxvZ3MubWVyZ2UpIDwtIGdzdWIoIi54IiwiLkI3MyIsbmFtZXMoY29tcGFyZS5idy5zeW50ZWxvZ3MubWVyZ2UpLGZpeGVkID0gVCkKbmFtZXMoY29tcGFyZS5idy5zeW50ZWxvZ3MubWVyZ2UpIDwtIGdzdWIoIi55IiwiLlcyMiIsbmFtZXMoY29tcGFyZS5idy5zeW50ZWxvZ3MubWVyZ2UpLGZpeGVkID0gVCkKCmNvbXBhcmUuYncuc3ludGVsb2dzLm1lcmdlJGltcHJpbnQuQjczW2lzLm5hKGNvbXBhcmUuYncuc3ludGVsb2dzLm1lcmdlJGltcHJpbnQuQjczKV0gPC0gIm5vLmltcHJpbnQiCmNvbXBhcmUuYncuc3ludGVsb2dzLm1lcmdlJGltcHJpbnQuVzIyW2lzLm5hKGNvbXBhcmUuYncuc3ludGVsb2dzLm1lcmdlJGltcHJpbnQuVzIyKV0gPC0gIm5vLmltcHJpbnQiCmNvbXBhcmUuYncuc3ludGVsb2dzLm1lcmdlJGZpbHRlci5pbXByaW50LkI3M1tpcy5uYShjb21wYXJlLmJ3LnN5bnRlbG9ncy5tZXJnZSRmaWx0ZXIuaW1wcmludC5CNzMpXSA8LSAibm8uaW1wcmludCIKY29tcGFyZS5idy5zeW50ZWxvZ3MubWVyZ2UkZmlsdGVyLmltcHJpbnQuVzIyW2lzLm5hKGNvbXBhcmUuYncuc3ludGVsb2dzLm1lcmdlJGZpbHRlci5pbXByaW50LlcyMildIDwtICJuby5pbXByaW50IgoKdGFibGUoY29tcGFyZS5idy5zeW50ZWxvZ3MubWVyZ2VbLGMoImZpbHRlci5pbXByaW50LkI3MyIsImZpbHRlci5pbXByaW50LlcyMiIpXSkKYGBgCgpVc2UgdG8gbWFrZSB2ZW5ucyAtIEI3MyB2cyBQSDIwNwpgYGB7cn0KY29tcGFyZS5icC5zeW50ZWxvZ3MgPC0gc3Vic2V0KHBsb3Qub3V0Mi5icCxwbG90Lm91dDIuYnAkZmVhdHVyZV90eXBlID09ICJnZW5lIikKCmNvbXBhcmUuYnAuc3ludGVsb2dzJHN5bnRlbG9nIDwtIGlmZWxzZShjb21wYXJlLmJwLnN5bnRlbG9ncyRmZWF0dXJlICVpbiUgZ2VuZS5rZXkuYnAuc3ludGVsb2dzJHY0X2dlbmVfbW9kZWwgfCBjb21wYXJlLmJwLnN5bnRlbG9ncyRmZWF0dXJlICVpbiUgZ2VuZS5rZXkuYnAuc3ludGVsb2dzJFBIMjA3LlptMDAwMDhhLjEuLCAic3ludGVsb2ciLCJub24uc3ludGVsb2ciKQoKdGFibGUoY29tcGFyZS5icC5zeW50ZWxvZ3NbLGMoImdlbm9tZSIsImZpbHRlci5pbXByaW50Iiwic3ludGVsb2ciKV0pCgpjb21wYXJlLmJwLnN5bnRlbG9ncy5iNzMgPC0gbWVyZ2Uoc3Vic2V0KGNvbXBhcmUuYnAuc3ludGVsb2dzLGNvbXBhcmUuYnAuc3ludGVsb2dzJHN5bnRlbG9nID09ICJzeW50ZWxvZyIgJiBjb21wYXJlLmJwLnN5bnRlbG9ncyRnZW5vbWUgPT0gIkI3MyIpLGdlbmUua2V5LmJwLnN5bnRlbG9ncyxieS54PSJmZWF0dXJlIixieS55PSJ2NF9nZW5lX21vZGVsIixhbGwueD1UKQpjb21wYXJlLmJwLnN5bnRlbG9ncy5QSDIwNyA8LSBzdWJzZXQoY29tcGFyZS5icC5zeW50ZWxvZ3MsY29tcGFyZS5icC5zeW50ZWxvZ3Mkc3ludGVsb2cgPT0gInN5bnRlbG9nIiAmIGNvbXBhcmUuYnAuc3ludGVsb2dzJGdlbm9tZSA9PSAiUEgyMDciKQpjb21wYXJlLmJwLnN5bnRlbG9ncy5tZXJnZSA8LSBtZXJnZShjb21wYXJlLmJwLnN5bnRlbG9ncy5iNzMsY29tcGFyZS5icC5zeW50ZWxvZ3MuUEgyMDcsYnkueCA9ICJQSDIwNy5abTAwMDA4YS4xLiIsYnkueT0iZmVhdHVyZSIsYWxsPVQpCgpuYW1lcyhjb21wYXJlLmJwLnN5bnRlbG9ncy5tZXJnZSkgPC0gZ3N1YigiLngiLCIuQjczIixuYW1lcyhjb21wYXJlLmJwLnN5bnRlbG9ncy5tZXJnZSksZml4ZWQgPSBUKQpuYW1lcyhjb21wYXJlLmJwLnN5bnRlbG9ncy5tZXJnZSkgPC0gZ3N1YigiLnkiLCIuUEgyMDciLG5hbWVzKGNvbXBhcmUuYnAuc3ludGVsb2dzLm1lcmdlKSxmaXhlZCA9IFQpCgpjb21wYXJlLmJwLnN5bnRlbG9ncy5tZXJnZSRpbXByaW50LkI3M1tpcy5uYShjb21wYXJlLmJwLnN5bnRlbG9ncy5tZXJnZSRpbXByaW50LkI3MyldIDwtICJuby5pbXByaW50Igpjb21wYXJlLmJwLnN5bnRlbG9ncy5tZXJnZSRpbXByaW50LlBIMjA3W2lzLm5hKGNvbXBhcmUuYnAuc3ludGVsb2dzLm1lcmdlJGltcHJpbnQuUEgyMDcpXSA8LSAibm8uaW1wcmludCIKY29tcGFyZS5icC5zeW50ZWxvZ3MubWVyZ2UkZmlsdGVyLmltcHJpbnQuQjczW2lzLm5hKGNvbXBhcmUuYnAuc3ludGVsb2dzLm1lcmdlJGZpbHRlci5pbXByaW50LkI3MyldIDwtICJuby5pbXByaW50Igpjb21wYXJlLmJwLnN5bnRlbG9ncy5tZXJnZSRmaWx0ZXIuaW1wcmludC5QSDIwN1tpcy5uYShjb21wYXJlLmJwLnN5bnRlbG9ncy5tZXJnZSRmaWx0ZXIuaW1wcmludC5QSDIwNyldIDwtICJuby5pbXByaW50IgoKdGFibGUoY29tcGFyZS5icC5zeW50ZWxvZ3MubWVyZ2VbLGMoImZpbHRlci5pbXByaW50LkI3MyIsImZpbHRlci5pbXByaW50LlBIMjA3IildKQpgYGAKCm1hdFRFIHZlbm5zCmBgYHtyfQp0ZS5hbm5vLmNvbXBhcmUgPC0gcmVhZC50YWJsZSgibm9uLXJlZHVuZGFudF9URXNfYnlfYW5ucHRhdGlvbl80Z2Vub21lc18yNEphbjIwLnR4dCIsaGVhZGVyID0gVCxzdHJpbmdzQXNGYWN0b3JzID0gRikKYGBgCkI3MyB2cyBXMjIgVEVzCmBgYHtyfQpzaGFyZWQuQlcgPC0gc3Vic2V0KHRlLmFubm8uY29tcGFyZSwgdGUuYW5uby5jb21wYXJlJEI3MyA9PSAicHJlc2VudCIgJiBuY2hhcih0ZS5hbm5vLmNvbXBhcmUkQjczLmFubm90YXRpb24gPT0gMjEpICYgdGUuYW5uby5jb21wYXJlJFcyMiA9PSAicHJlc2VudCIgJiBuY2hhcih0ZS5hbm5vLmNvbXBhcmUkVzIyLmFubm90YXRpb24gPT0gMjEpKQoKY29tcGFyZS5idy5URSA8LSBzdWJzZXQocGxvdC5vdXQyLmJ3LHBsb3Qub3V0Mi5idyRmZWF0dXJlX3R5cGUgPT0gIlRFIiAmIHBsb3Qub3V0Mi5idyRmaWx0ZXIuaW1wcmludCA9PSAiTUVHIikKY29tcGFyZS5idy5URSRzaGFyZWQgPC0gaWZlbHNlKGNvbXBhcmUuYncuVEUkZmVhdHVyZSAlaW4lIHNoYXJlZC5CVyRCNzMuYW5ub3RhdGlvbiB8IGNvbXBhcmUuYncuVEUkZmVhdHVyZSAlaW4lIHNoYXJlZC5CVyRXMjIuYW5ub3RhdGlvbiwgInNoYXJlZCIsInZhcmlhYmxlIikKCnRhYmxlKGNvbXBhcmUuYncuVEVbLGMoImdlbm9tZSIsImZpbHRlci5pbXByaW50Iiwic2hhcmVkIildKQoKY29tcGFyZS5idy5URS5iIDwtIG1lcmdlKHN1YnNldChjb21wYXJlLmJ3LlRFLGNvbXBhcmUuYncuVEUkc2hhcmVkID09ICJzaGFyZWQiICYgY29tcGFyZS5idy5URSRnZW5vbWUgPT0gIkI3MyIpLHNoYXJlZC5CV1ssYygiQjczLmFubm90YXRpb24iLCJXMjIuYW5ub3RhdGlvbiIpXSxieS54PSJmZWF0dXJlIixieS55PSJCNzMuYW5ub3RhdGlvbiIsYWxsLng9VCkKY29tcGFyZS5idy5URS53IDwtIHN1YnNldChjb21wYXJlLmJ3LlRFLGNvbXBhcmUuYncuVEUkc2hhcmVkID09ICJzaGFyZWQiICYgY29tcGFyZS5idy5URSRnZW5vbWUgPT0gIlcyMiIpCmNvbXBhcmUuYncuVEUubWVyZ2UgPC0gbWVyZ2UoY29tcGFyZS5idy5URS5iLCBjb21wYXJlLmJ3LlRFLncsIGJ5Lng9IlcyMi5hbm5vdGF0aW9uIixieS55PSJmZWF0dXJlIixhbGw9VCkKCm5hbWVzKGNvbXBhcmUuYncuVEUubWVyZ2UpIDwtIGdzdWIoIi54IiwiLkI3MyIsbmFtZXMoY29tcGFyZS5idy5URS5tZXJnZSksZml4ZWQgPSBUKQpuYW1lcyhjb21wYXJlLmJ3LlRFLm1lcmdlKSA8LSBnc3ViKCIueSIsIi5XMjIiLG5hbWVzKGNvbXBhcmUuYncuVEUubWVyZ2UpLGZpeGVkID0gVCkKCmNvbXBhcmUuYncuVEUubWVyZ2UkZmlsdGVyLmltcHJpbnQuQjczW2lzLm5hKGNvbXBhcmUuYncuVEUubWVyZ2UkZmlsdGVyLmltcHJpbnQuQjczKV0gPC0gIm5vLmltcHJpbnQiCmNvbXBhcmUuYncuVEUubWVyZ2UkZmlsdGVyLmltcHJpbnQuVzIyW2lzLm5hKGNvbXBhcmUuYncuVEUubWVyZ2UkZmlsdGVyLmltcHJpbnQuVzIyKV0gPC0gIm5vLmltcHJpbnQiCgp0YWJsZShjb21wYXJlLmJ3LlRFLm1lcmdlWyxjKCJmaWx0ZXIuaW1wcmludC5CNzMiLCJmaWx0ZXIuaW1wcmludC5XMjIiKV0pCmBgYAoKCkI3MyB2cyBQSDIwNyBURXMKYGBge3J9CnNoYXJlZC5icCA8LSBzdWJzZXQodGUuYW5uby5jb21wYXJlLCB0ZS5hbm5vLmNvbXBhcmUkQjczID09ICJwcmVzZW50IiAmIG5jaGFyKHRlLmFubm8uY29tcGFyZSRCNzMuYW5ub3RhdGlvbikgPT0gMjEgJiB0ZS5hbm5vLmNvbXBhcmUkUEgyMDcgPT0gInByZXNlbnQiICYgbmNoYXIodGUuYW5uby5jb21wYXJlJFBIMjA3LmFubm90YXRpb24pID09IDIxKQoKY29tcGFyZS5icC5URSA8LSBzdWJzZXQocGxvdC5vdXQyLmJwLHBsb3Qub3V0Mi5icCRmZWF0dXJlX3R5cGUgPT0gIlRFIiAmIHBsb3Qub3V0Mi5icCRmaWx0ZXIuaW1wcmludCA9PSAiTUVHIikKY29tcGFyZS5icC5URSRzaGFyZWQgPC0gaWZlbHNlKGNvbXBhcmUuYnAuVEUkZmVhdHVyZSAlaW4lIHNoYXJlZC5icCRCNzMuYW5ub3RhdGlvbiB8IGNvbXBhcmUuYnAuVEUkZmVhdHVyZSAlaW4lIHNoYXJlZC5icCRQMjA3LmFubm90YXRpb24sICJzaGFyZWQiLCJ2YXJpYWJsZSIpCgp0YWJsZShjb21wYXJlLmJwLlRFWyxjKCJnZW5vbWUiLCJmaWx0ZXIuaW1wcmludCIsInNoYXJlZCIpXSkKCmNvbXBhcmUuYnAuVEUuYiA8LSBtZXJnZShzdWJzZXQoY29tcGFyZS5icC5URSxjb21wYXJlLmJwLlRFJHNoYXJlZCA9PSAic2hhcmVkIiAmIGNvbXBhcmUuYnAuVEUkZ2Vub21lID09ICJCNzMiKSxzaGFyZWQuYnBbLGMoIkI3My5hbm5vdGF0aW9uIiwiUEgyMDcuYW5ub3RhdGlvbiIpXSxieS54PSJmZWF0dXJlIixieS55PSJCNzMuYW5ub3RhdGlvbiIsYWxsLng9VCkKY29tcGFyZS5icC5URS53IDwtIHN1YnNldChjb21wYXJlLmJwLlRFLGNvbXBhcmUuYnAuVEUkc2hhcmVkID09ICJzaGFyZWQiICYgY29tcGFyZS5icC5URSRnZW5vbWUgPT0gIlBIMjA3IikKY29tcGFyZS5icC5URS5tZXJnZSA8LSBtZXJnZShjb21wYXJlLmJwLlRFLmIsIGNvbXBhcmUuYnAuVEUudywgYnkueD0iUEgyMDcuYW5ub3RhdGlvbiIsYnkueT0iZmVhdHVyZSIsYWxsPVQpCgpuYW1lcyhjb21wYXJlLmJwLlRFLm1lcmdlKSA8LSBnc3ViKCIueCIsIi5CNzMiLG5hbWVzKGNvbXBhcmUuYnAuVEUubWVyZ2UpLGZpeGVkID0gVCkKbmFtZXMoY29tcGFyZS5icC5URS5tZXJnZSkgPC0gZ3N1YigiLnkiLCIuUEgyMDciLG5hbWVzKGNvbXBhcmUuYnAuVEUubWVyZ2UpLGZpeGVkID0gVCkKCmNvbXBhcmUuYnAuVEUubWVyZ2UkZmlsdGVyLmltcHJpbnQuQjczW2lzLm5hKGNvbXBhcmUuYnAuVEUubWVyZ2UkZmlsdGVyLmltcHJpbnQuQjczKV0gPC0gIm5vLmltcHJpbnQiCmNvbXBhcmUuYnAuVEUubWVyZ2UkZmlsdGVyLmltcHJpbnQuUEgyMDdbaXMubmEoY29tcGFyZS5icC5URS5tZXJnZSRmaWx0ZXIuaW1wcmludC5QSDIwNyldIDwtICJuby5pbXByaW50IgoKdGFibGUoY29tcGFyZS5icC5URS5tZXJnZVssYygiZmlsdGVyLmltcHJpbnQuQjczIiwiZmlsdGVyLmltcHJpbnQuUEgyMDciKV0pCmBgYAoKSG93IG1hbnkgTUVHcyBjYWxsZWQgaW4gb25seSBvbmUgZGlyZWN0aW9uIHdlcmUgZHVlIHRvIGN1dG9mZnMgYW5kIG5vIGRhdGE/IFVzZSBCNzMgYnkgVzIyIGNvbnRyYXN0IGZvciBudW1iZXJzCmBgYHtyfQpCNzMub25seS5NRUcuYncgPC0gc3Vic2V0KGNvbXBhcmUuYncuc3ludGVsb2dzLm1lcmdlLGNvbXBhcmUuYncuc3ludGVsb2dzLm1lcmdlJGZpbHRlci5pbXByaW50LkI3MyA9PSAiTUVHIiAmIGNvbXBhcmUuYncuc3ludGVsb2dzLm1lcmdlJGZpbHRlci5pbXByaW50LlcyMiA9PSAibm8uaW1wcmludCIpCkI3My5vbmx5Lk1FRy5idyRjYXRlZ29yeSA8LSBpZmVsc2UoaXMubmEoQjczLm9ubHkuTUVHLmJ3JG1hdGVybmFsX3ByZWZlcmVuY2UuVzIyKSwibG93LmNvdmVyYWdlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoQjczLm9ubHkuTUVHLmJ3JG1hdGVybmFsX3ByZWZlcmVuY2UuVzIyID4gMC43NSwiUkVSPjAuNzUiLCJuby5pbXByaW50IikpCkI3My5vbmx5Lk1FRy5idyRncm91cCA8LSAiQjczLk1FRy5CVyIKClcyMi5vbmx5Lk1FRy5idyA8LSBzdWJzZXQoY29tcGFyZS5idy5zeW50ZWxvZ3MubWVyZ2UsY29tcGFyZS5idy5zeW50ZWxvZ3MubWVyZ2UkZmlsdGVyLmltcHJpbnQuQjczID09ICJuby5pbXByaW50IiAmIGNvbXBhcmUuYncuc3ludGVsb2dzLm1lcmdlJGZpbHRlci5pbXByaW50LlcyMiA9PSAiTUVHIikKVzIyLm9ubHkuTUVHLmJ3JGNhdGVnb3J5IDwtIGlmZWxzZShpcy5uYShXMjIub25seS5NRUcuYnckbWF0ZXJuYWxfcHJlZmVyZW5jZS5CNzMpLCJsb3cuY292ZXJhZ2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShXMjIub25seS5NRUcuYnckbWF0ZXJuYWxfcHJlZmVyZW5jZS5CNzMgPiAwLjc1LCJSRVI+MC43NSIsIm5vLmltcHJpbnQiKSkKVzIyLm9ubHkuTUVHLmJ3JGdyb3VwIDwtICJXMjIuTUVHLkJXIgoKQjczLm9ubHkuTUVHLmJwIDwtIHN1YnNldChjb21wYXJlLmJwLnN5bnRlbG9ncy5tZXJnZSxjb21wYXJlLmJwLnN5bnRlbG9ncy5tZXJnZSRmaWx0ZXIuaW1wcmludC5CNzMgPT0gIk1FRyIgJiBjb21wYXJlLmJwLnN5bnRlbG9ncy5tZXJnZSRmaWx0ZXIuaW1wcmludC5QSDIwNyA9PSAibm8uaW1wcmludCIpCkI3My5vbmx5Lk1FRy5icCRjYXRlZ29yeSA8LSBpZmVsc2UoaXMubmEoQjczLm9ubHkuTUVHLmJwJG1hdGVybmFsX3ByZWZlcmVuY2UuUEgyMDcpLCJsb3cuY292ZXJhZ2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShCNzMub25seS5NRUcuYnAkbWF0ZXJuYWxfcHJlZmVyZW5jZS5QSDIwNyA+IDAuNzUsIlJFUj4wLjc1Iiwibm8uaW1wcmludCIpKQpCNzMub25seS5NRUcuYnAkZ3JvdXAgPC0gIkI3My5NRUcuQlAiCgpQSDIwNy5vbmx5Lk1FRy5icCA8LSBzdWJzZXQoY29tcGFyZS5icC5zeW50ZWxvZ3MubWVyZ2UsY29tcGFyZS5icC5zeW50ZWxvZ3MubWVyZ2UkZmlsdGVyLmltcHJpbnQuQjczID09ICJuby5pbXByaW50IiAmIGNvbXBhcmUuYnAuc3ludGVsb2dzLm1lcmdlJGZpbHRlci5pbXByaW50LlBIMjA3ID09ICJNRUciKQpQSDIwNy5vbmx5Lk1FRy5icCRjYXRlZ29yeSA8LSBpZmVsc2UoaXMubmEoUEgyMDcub25seS5NRUcuYnAkbWF0ZXJuYWxfcHJlZmVyZW5jZS5CNzMpLCJsb3cuY292ZXJhZ2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShQSDIwNy5vbmx5Lk1FRy5icCRtYXRlcm5hbF9wcmVmZXJlbmNlLkI3MyA+IDAuNzUsIlJFUj4wLjc1Iiwibm8uaW1wcmludCIpKQpQSDIwNy5vbmx5Lk1FRy5icCRncm91cCA8LSAiUEgyMDcuTUVHLkJQIgoKCm5vdC5lbm91Z2gubWF0LmJpYXMgPC0gc3VtKG5yb3coc3Vic2V0KEI3My5vbmx5Lk1FRy5idyxCNzMub25seS5NRUcuYnckbWF0ZXJuYWxfcHJlZmVyZW5jZS5XMjIgPiAwLjc1KSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgIG5yb3coc3Vic2V0KFcyMi5vbmx5Lk1FRy5idyxXMjIub25seS5NRUcuYnckbWF0ZXJuYWxfcHJlZmVyZW5jZS5CNzMgPiAwLjc1KSkpCnRvdGFsLm5vbi5vdmVybGFwIDwtIHN1bShucm93KEI3My5vbmx5Lk1FRy5idyksbnJvdyhXMjIub25seS5NRUcuYncpKQpub3QuZW5vdWdoLm1hdC5iaWFzIC8gdG90YWwubm9uLm92ZXJsYXAgKiAxMDAKCm5vdC5lbm91Z2gubWF0LmRhdGEgPC0gc3VtKG5yb3coc3Vic2V0KEI3My5vbmx5Lk1FRy5idyxpcy5uYShCNzMub25seS5NRUcuYnckbWF0ZXJuYWxfcHJlZmVyZW5jZS5XMjIpKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgIG5yb3coc3Vic2V0KFcyMi5vbmx5Lk1FRy5idyxpcy5uYShXMjIub25seS5NRUcuYnckbWF0ZXJuYWxfcHJlZmVyZW5jZS5CNzMpKSkpCm5vdC5lbm91Z2gubWF0LmRhdGEgLyB0b3RhbC5ub24ub3ZlcmxhcCAqIDEwMAoKTUVHLm5vbi5vdmVybGFwIDwtIHJiaW5kKEI3My5vbmx5Lk1FRy5id1ssYygiZ3JvdXAiLCJjYXRlZ29yeSIpXSxXMjIub25seS5NRUcuYndbLGMoImdyb3VwIiwiY2F0ZWdvcnkiKV0sQjczLm9ubHkuTUVHLmJwWyxjKCJncm91cCIsImNhdGVnb3J5IildLFBIMjA3Lm9ubHkuTUVHLmJwWyxjKCJncm91cCIsImNhdGVnb3J5IildKQpwbG90Lk1FRy5ub24ub3ZlcmxhcCA8LSBkYXRhLmZyYW1lKHRhYmxlKE1FRy5ub24ub3ZlcmxhcCkpCnBsb3QuTUVHLm5vbi5vdmVybGFwJGNhdGVnb3J5IDwtIGZhY3RvcihwbG90Lk1FRy5ub24ub3ZlcmxhcCRjYXRlZ29yeSwgbGV2ZWxzPWMoIm5vLmltcHJpbnQiLCJsb3cuY292ZXJhZ2UiLCJSRVI+MC43NSIpKQpwbG90Lk1FRy5ub24ub3ZlcmxhcCRncm91cCA8LSBmYWN0b3IocGxvdC5NRUcubm9uLm92ZXJsYXAkZ3JvdXAsbGV2ZWxzPWMoIkI3My5NRUcuQlciLCJXMjIuTUVHLkJXIiwiQjczLk1FRy5CUCIsIlBIMjA3Lk1FRy5CUCIpKQoKZ2dwbG90KCkgKyBnZW9tX2JhcihhZXMoeD1ncm91cCx5PUZyZXEsZmlsbD1jYXRlZ29yeSksZGF0YT1wbG90Lk1FRy5ub24ub3ZlcmxhcCxzdGF0PSJpZGVudGl0eSIscG9zaXRpb249cG9zaXRpb25fZmlsbCgpKSArIHRoZW1lX2NsYXNzaWMoKSAgKyAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGU9OTAsIGhqdXN0PTEpKSArIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGU9IlJlZHMiKQpgYGAKV2hhdCBhYm91dCBQRUdzPyBVc2UgQjczIGJ5IFcyMiBjb250cmFzdCBmb3IgbnVtYmVycwpgYGB7cn0KQjczLm9ubHkuUEVHLmJ3IDwtIHN1YnNldChjb21wYXJlLmJ3LnN5bnRlbG9ncy5tZXJnZSxjb21wYXJlLmJ3LnN5bnRlbG9ncy5tZXJnZSRmaWx0ZXIuaW1wcmludC5CNzMgPT0gIlBFRyIgJiBjb21wYXJlLmJ3LnN5bnRlbG9ncy5tZXJnZSRmaWx0ZXIuaW1wcmludC5XMjIgPT0gIm5vLmltcHJpbnQiKQpCNzMub25seS5QRUcuYnckY2F0ZWdvcnkgPC0gaWZlbHNlKGlzLm5hKEI3My5vbmx5LlBFRy5idyRtYXRlcm5hbF9wcmVmZXJlbmNlLlcyMiksImxvdy5jb3ZlcmFnZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKEI3My5vbmx5LlBFRy5idyRtYXRlcm5hbF9wcmVmZXJlbmNlLlcyMiA8IDAuMjUsIlJFUjwwLjI1Iiwibm8uaW1wcmludCIpKQpCNzMub25seS5QRUcuYnckZ3JvdXAgPC0gIkI3My5QRUcuQlciCgpXMjIub25seS5QRUcuYncgPC0gc3Vic2V0KGNvbXBhcmUuYncuc3ludGVsb2dzLm1lcmdlLGNvbXBhcmUuYncuc3ludGVsb2dzLm1lcmdlJGZpbHRlci5pbXByaW50LkI3MyA9PSAibm8uaW1wcmludCIgJiBjb21wYXJlLmJ3LnN5bnRlbG9ncy5tZXJnZSRmaWx0ZXIuaW1wcmludC5XMjIgPT0gIlBFRyIpClcyMi5vbmx5LlBFRy5idyRjYXRlZ29yeSA8LSBpZmVsc2UoaXMubmEoVzIyLm9ubHkuUEVHLmJ3JG1hdGVybmFsX3ByZWZlcmVuY2UuQjczKSwibG93LmNvdmVyYWdlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoVzIyLm9ubHkuUEVHLmJ3JG1hdGVybmFsX3ByZWZlcmVuY2UuQjczIDwgMC4yNSwiUkVSPDAuMjUiLCJuby5pbXByaW50IikpClcyMi5vbmx5LlBFRy5idyRncm91cCA8LSAiVzIyLlBFRy5CVyIKCkI3My5vbmx5LlBFRy5icCA8LSBzdWJzZXQoY29tcGFyZS5icC5zeW50ZWxvZ3MubWVyZ2UsY29tcGFyZS5icC5zeW50ZWxvZ3MubWVyZ2UkZmlsdGVyLmltcHJpbnQuQjczID09ICJQRUciICYgY29tcGFyZS5icC5zeW50ZWxvZ3MubWVyZ2UkZmlsdGVyLmltcHJpbnQuUEgyMDcgPT0gIm5vLmltcHJpbnQiKQpCNzMub25seS5QRUcuYnAkY2F0ZWdvcnkgPC0gaWZlbHNlKGlzLm5hKEI3My5vbmx5LlBFRy5icCRtYXRlcm5hbF9wcmVmZXJlbmNlLlBIMjA3KSwibG93LmNvdmVyYWdlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoQjczLm9ubHkuUEVHLmJwJG1hdGVybmFsX3ByZWZlcmVuY2UuUEgyMDcgPCAwLjI1LCJSRVI8MC4yNSIsIm5vLmltcHJpbnQiKSkKQjczLm9ubHkuUEVHLmJwJGdyb3VwIDwtICJCNzMuUEVHLkJQIgoKUEgyMDcub25seS5QRUcuYnAgPC0gc3Vic2V0KGNvbXBhcmUuYnAuc3ludGVsb2dzLm1lcmdlLGNvbXBhcmUuYnAuc3ludGVsb2dzLm1lcmdlJGZpbHRlci5pbXByaW50LkI3MyA9PSAibm8uaW1wcmludCIgJiBjb21wYXJlLmJwLnN5bnRlbG9ncy5tZXJnZSRmaWx0ZXIuaW1wcmludC5QSDIwNyA9PSAiUEVHIikKUEgyMDcub25seS5QRUcuYnAkY2F0ZWdvcnkgPC0gaWZlbHNlKGlzLm5hKFBIMjA3Lm9ubHkuUEVHLmJwJG1hdGVybmFsX3ByZWZlcmVuY2UuQjczKSwibG93LmNvdmVyYWdlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoUEgyMDcub25seS5QRUcuYnAkbWF0ZXJuYWxfcHJlZmVyZW5jZS5CNzMgPCAwLjI1LCJSRVI8MC4yNSIsIm5vLmltcHJpbnQiKSkKUEgyMDcub25seS5QRUcuYnAkZ3JvdXAgPC0gIlBIMjA3LlBFRy5CUCIKCgpub3QuZW5vdWdoLnBhdC5iaWFzIDwtIHN1bShucm93KHN1YnNldChCNzMub25seS5QRUcuYncsQjczLm9ubHkuUEVHLmJ3JG1hdGVybmFsX3ByZWZlcmVuY2UuVzIyIDwgMC4yNSkpLAogICAgICAgICAgICAgICAgICAgICAgICAgICBucm93KHN1YnNldChXMjIub25seS5QRUcuYncsVzIyLm9ubHkuUEVHLmJ3JG1hdGVybmFsX3ByZWZlcmVuY2UuQjczID4gMC4yNSkpKQp0b3RhbC5ub24ub3ZlcmxhcC5wYXQgPC0gc3VtKG5yb3coQjczLm9ubHkuUEVHLmJ3KSxucm93KFcyMi5vbmx5LlBFRy5idykpCm5vdC5lbm91Z2gucGF0LmJpYXMgLyB0b3RhbC5ub24ub3ZlcmxhcC5wYXQgKiAxMDAKCm5vdC5lbm91Z2gucGF0LmRhdGEgPC0gc3VtKG5yb3coc3Vic2V0KEI3My5vbmx5LlBFRy5idyxpcy5uYShCNzMub25seS5QRUcuYnckbWF0ZXJuYWxfcHJlZmVyZW5jZS5XMjIpKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgIG5yb3coc3Vic2V0KFcyMi5vbmx5LlBFRy5idyxpcy5uYShXMjIub25seS5QRUcuYnckbWF0ZXJuYWxfcHJlZmVyZW5jZS5CNzMpKSkpCm5vdC5lbm91Z2gucGF0LmRhdGEgLyB0b3RhbC5ub24ub3ZlcmxhcC5wYXQgKiAxMDAKClBFRy5ub24ub3ZlcmxhcCA8LSByYmluZChCNzMub25seS5QRUcuYndbLGMoImdyb3VwIiwiY2F0ZWdvcnkiKV0sVzIyLm9ubHkuUEVHLmJ3WyxjKCJncm91cCIsImNhdGVnb3J5IildLEI3My5vbmx5LlBFRy5icFssYygiZ3JvdXAiLCJjYXRlZ29yeSIpXSxQSDIwNy5vbmx5LlBFRy5icFssYygiZ3JvdXAiLCJjYXRlZ29yeSIpXSkKcGxvdC5QRUcubm9uLm92ZXJsYXAgPC0gZGF0YS5mcmFtZSh0YWJsZShQRUcubm9uLm92ZXJsYXApKQpwbG90LlBFRy5ub24ub3ZlcmxhcCRjYXRlZ29yeSA8LSBmYWN0b3IocGxvdC5QRUcubm9uLm92ZXJsYXAkY2F0ZWdvcnksIGxldmVscz1jKCJuby5pbXByaW50IiwibG93LmNvdmVyYWdlIiwiUkVSPDAuMjUiKSkKcGxvdC5QRUcubm9uLm92ZXJsYXAkZ3JvdXAgPC0gZmFjdG9yKHBsb3QuUEVHLm5vbi5vdmVybGFwJGdyb3VwLGxldmVscz1jKCJCNzMuUEVHLkJXIiwiVzIyLlBFRy5CVyIsIkI3My5QRUcuQlAiLCJQSDIwNy5QRUcuQlAiKSkKCmdncGxvdCgpICsgZ2VvbV9iYXIoYWVzKHg9Z3JvdXAseT1GcmVxLGZpbGw9Y2F0ZWdvcnkpLGRhdGE9cGxvdC5QRUcubm9uLm92ZXJsYXAsc3RhdD0iaWRlbnRpdHkiLHBvc2l0aW9uPXBvc2l0aW9uX2ZpbGwoKSkgKyB0aGVtZV9jbGFzc2ljKCkgICsgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlPTkwLCBoanVzdD0xKSkgKyBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlPSJCbHVlcyIpCmBgYApDb21iaW5lZApgYGB7cn0KYSA8LSBnZ3Bsb3QoKSArIGdlb21fYmFyKGFlcyh4PWdyb3VwLHk9RnJlcSxmaWxsPWNhdGVnb3J5KSxkYXRhPXBsb3QuTUVHLm5vbi5vdmVybGFwLHN0YXQ9ImlkZW50aXR5Iixwb3NpdGlvbj1wb3NpdGlvbl9maWxsKCkpICsgdGhlbWVfY2xhc3NpYygpICArICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZT05MCwgaGp1c3Q9MSkpICsgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZT0iUmVkcyIpCmIgPC0gZ2dwbG90KCkgKyBnZW9tX2JhcihhZXMoeD1ncm91cCx5PUZyZXEsZmlsbD1jYXRlZ29yeSksZGF0YT1wbG90LlBFRy5ub24ub3ZlcmxhcCxzdGF0PSJpZGVudGl0eSIscG9zaXRpb249cG9zaXRpb25fZmlsbCgpKSArIHRoZW1lX2NsYXNzaWMoKSAgKyAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGU9OTAsIGhqdXN0PTEpKSArIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGU9IkJsdWVzIikKCmdyaWQuYXJyYW5nZShhLGIsbnJvdz0xKQpgYGAKCgoKCkNyZWF0ZSBmaWxlIG9mIEJXIGNhbGxzIApgYGB7cn0KdG1wLm1lZyA8LSBzdWJzZXQoY29tcGFyZS5idy5zeW50ZWxvZ3MubWVyZ2UsY29tcGFyZS5idy5zeW50ZWxvZ3MubWVyZ2UkZmlsdGVyLmltcHJpbnQuQjczID09ICJNRUciICYgY29tcGFyZS5idy5zeW50ZWxvZ3MubWVyZ2UkZmlsdGVyLmltcHJpbnQuVzIyID09ICJNRUciKQp0bXAucGVnIDwtIHN1YnNldChjb21wYXJlLmJ3LnN5bnRlbG9ncy5tZXJnZSxjb21wYXJlLmJ3LnN5bnRlbG9ncy5tZXJnZSRmaWx0ZXIuaW1wcmludC5CNzMgPT0gIlBFRyIgJiBjb21wYXJlLmJ3LnN5bnRlbG9ncy5tZXJnZSRmaWx0ZXIuaW1wcmludC5XMjIgPT0gIlBFRyIpCnRtcDMgPC0gc3Vic2V0KGNvbXBhcmUuYncuc3ludGVsb2dzLm1lcmdlLGNvbXBhcmUuYncuc3ludGVsb2dzLm1lcmdlJGZpbHRlci5pbXByaW50LkI3MyAhPSBjb21wYXJlLmJ3LnN5bnRlbG9ncy5tZXJnZSRmaWx0ZXIuaW1wcmludC5XMjIpCgpidy5vdXQuc2hhcmUgPC0gcGxvdC5vdXQyLmJ3CmJ3Lm91dC5zaGFyZSRzeW50ZWxvZyA8LSBpZmVsc2UoYncub3V0LnNoYXJlJGZlYXR1cmVfdHlwZSA9PSAiVEUiLCAibmEudGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShidy5vdXQuc2hhcmUkZmVhdHVyZSAlaW4lIGdlbmUua2V5LkJXLnN5bnRlbG9ncyR2NF9nZW5lX21vZGVsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgYncub3V0LnNoYXJlJGZlYXR1cmUgJWluJSBnZW5lLmtleS5CVy5zeW50ZWxvZ3MkVzIyLlptMDAwMDRiLjEuLCAic3ludGVsb2ciLCJub24uc3ludGVsb2ciKSkKYncub3V0LnNoYXJlJGltcHJpbnRlZC5ib3RoIDwtIGlmZWxzZShidy5vdXQuc2hhcmUkc3ludGVsb2cgPT0gIm5hLnRlIiB8IGJ3Lm91dC5zaGFyZSRzeW50ZWxvZyA9PSAibm9uLnN5bnRlbG9nIixidy5vdXQuc2hhcmUkc3ludGVsb2csCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKGJ3Lm91dC5zaGFyZSRmZWF0dXJlICVpbiUgdG1wLm1lZyRmZWF0dXJlIHwgYncub3V0LnNoYXJlJGZlYXR1cmUgJWluJSB0bXAubWVnJFcyMi5abTAwMDA0Yi4xLiwiYm90aC5tZWdzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoYncub3V0LnNoYXJlJGZlYXR1cmUgJWluJSB0bXAucGVnJGZlYXR1cmUgfCBidy5vdXQuc2hhcmUkZmVhdHVyZSAlaW4lIHRtcC5wZWckVzIyLlptMDAwMDRiLjEuLCJib3RoLnBlZ3MiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoYncub3V0LnNoYXJlJGZlYXR1cmUgJWluJSB0bXAzJGZlYXR1cmUgfCBidy5vdXQuc2hhcmUkZmVhdHVyZSAlaW4lIHRtcDMkVzIyLlptMDAwMDRiLjEuLCJpbmNvbnNpc3RlbnQuaW1wcmludCIsIm5vLmltcHJpbnQiKSkpKQpgYGAKCmBgYHtyfQp0bXAxIDwtIGJ3Lm91dC5zaGFyZVtidy5vdXQuc2hhcmUkZ2Vub21lID09ICJCNzMiLGMoImZlYXR1cmUiLCJmaWx0ZXIuaW1wcmludCIsInN5bnRlbG9nIiwiaW1wcmludGVkLmJvdGgiKV0KbmFtZXModG1wMSlbMjo0XSA8LSBjKCJmaWx0ZXIuaW1wcmludC5CVyIsInN5bnRlbG9nLkJXIiwiaW1wcmludGVkLmJvdGguQlciKQp0bXAyIDwtIHBsb3Qub3V0Mi5icFtwbG90Lm91dDIuYnAkZ2Vub21lID09ICJCNzMiLGMoImZlYXR1cmUiLCJmaWx0ZXIuaW1wcmludCIpXQpuYW1lcyh0bXAyKVsyXSA8LSAiZmlsdGVyLmltcHJpbnQuQlAiCnRtcDIkc3ludGVsb2cuQlAgPC0gaWZlbHNlKG5jaGFyKHRtcDIkZmVhdHVyZSkgPT0gMjEsICJuYS50ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKHRtcDIkZmVhdHVyZSAlaW4lIGdlbmUua2V5LmJwLnN5bnRlbG9ncyR2NF9nZW5lX21vZGVsLCAic3ludGVsb2ciLCJub24uc3ludGVsb2ciKSkKYi5ib3RoLmNvbnRyYXN0cyA8LSBtZXJnZSh0bXAxLHRtcDIsYnk9ImZlYXR1cmUiLGFsbD1UKQpiLmJvdGguY29udHJhc3RzJGZpbHRlci5pbXByaW50LkJXW2lzLm5hKGIuYm90aC5jb250cmFzdHMkZmlsdGVyLmltcHJpbnQuQlcpXSA8LSAibm90LmRldGVjdGVkIgpiLmJvdGguY29udHJhc3RzJGZpbHRlci5pbXByaW50LkJQW2lzLm5hKGIuYm90aC5jb250cmFzdHMkZmlsdGVyLmltcHJpbnQuQlApXSA8LSAibm90LmRldGVjdGVkIgpiLmJvdGguY29udHJhc3RzW2lzLm5hKGIuYm90aC5jb250cmFzdHMpXSA8LSAiTkEiCmIuYm90aC5jb250cmFzdHMkZmVhdHVyZV90eXBlIDwtIGlmZWxzZShuY2hhcihiLmJvdGguY29udHJhc3RzJGZlYXR1cmUpID09IDIxLCAiVEUiLCJnZW5lIikKYi5ib3RoLmNvbnRyYXN0cyRpbXByaW50IDwtIGlmZWxzZShiLmJvdGguY29udHJhc3RzJGZlYXR1cmVfdHlwZSA9PSAiVEUiICYgKGIuYm90aC5jb250cmFzdHMkZmlsdGVyLmltcHJpbnQuQlAgPT0gIk1FRyIgfCBiLmJvdGguY29udHJhc3RzJGZpbHRlci5pbXByaW50LkJXID09ICJNRUciKSwibWF0VEUiLAogICAgICAgICAgICAgICAgaWZlbHNlKGIuYm90aC5jb250cmFzdHMkZmlsdGVyLmltcHJpbnQuQlAgPT0gIk1FRyIgfCBiLmJvdGguY29udHJhc3RzJGZpbHRlci5pbXByaW50LkJXID09ICJNRUciLCJNRUciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoYi5ib3RoLmNvbnRyYXN0cyRmZWF0dXJlX3R5cGUgPT0gImdlbmUiICYgKGIuYm90aC5jb250cmFzdHMkZmlsdGVyLmltcHJpbnQuQlAgPT0gIlBFRyIgfCBiLmJvdGguY29udHJhc3RzJGZpbHRlci5pbXByaW50LkJXID09ICJQRUciKSwiUEVHIiwibm8uaW1wcmludCIpKSkKYi5ib3RoLmNvbnRyYXN0cyRzeW50ZWxvZyA8LSBpZmVsc2UoYi5ib3RoLmNvbnRyYXN0cyRmZWF0dXJlID09ICJURSIsICJuYS5URSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShiLmJvdGguY29udHJhc3RzJGZlYXR1cmUgJWluJSBnZW5lLmtleS5CV1AkdjRfZ2VuZV9tb2RlbCwgImNvbnNlcnZlZC5tYWl6ZSIsInZhcmlhYmxlIikpCnRhYmxlKGIuYm90aC5jb250cmFzdHNbLGMoImZpbHRlci5pbXByaW50LkJXIiwiZmlsdGVyLmltcHJpbnQuQlAiKV0pCmBgYApXaGF0IHByb3BvcnRpb24gb2YgdGhlIHN5bnRlbG9nIHZzIG5vbi1zeW50ZWxvZ3MgYWdyZWUgYWNyb3NzIGNvbnRyYXN0cz8KYGBge3J9CmIuYm90aC5jb250cmFzdHMkYWdyZWVtZW50IDwtIGlmZWxzZShiLmJvdGguY29udHJhc3RzJGZpbHRlci5pbXByaW50LkJQID09IGIuYm90aC5jb250cmFzdHMkZmlsdGVyLmltcHJpbnQuQlcsICJtYXRjaGVkLmNhbGwiLCJtaXNtYXRjaGVkX2NhbGwiKQpzeW50ZWxvZy5hZ3JlZW1lbnQgPC0gZGF0YS5mcmFtZShiLmJvdGguY29udHJhc3RzICU+JSBncm91cF9ieShzeW50ZWxvZyxhZ3JlZW1lbnQsaW1wcmludCkgJT4lIGRwbHlyOjpzdW1tYXJpc2UoZmVhdHVyZXMgPSBuKCkpKQoKZ2dwbG90KCkgKyBnZW9tX2JhcihhZXMoeD1zeW50ZWxvZyx5PWZlYXR1cmVzLGZpbGw9YWdyZWVtZW50KSxkYXRhPXN1YnNldChzeW50ZWxvZy5hZ3JlZW1lbnQsc3ludGVsb2cuYWdyZWVtZW50JGltcHJpbnQgIT0gIm5vLmltcHJpbnQiKSxzdGF0PSJpZGVudGl0eSIscG9zaXRpb24gPSBwb3NpdGlvbl9maWxsKCkpICsgdGhlbWVfY2xhc3NpYygpICArICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZT05MCwgaGp1c3Q9MSkpICsgZmFjZXRfZ3JpZCgufmltcHJpbnQsc2NhbGVzPSJmcmVlIikKYGBgCgoKCkNvbXBhcmUgdG8gU05QLUFTRSBtZXRob2QKYGBge3J9CnRtcCA8LSBzdWJzZXQoYXNlLnJlYWRzMixhc2UucmVhZHMyJGNvbnRyYXN0ID09ICJCVyIgJiBhc2UucmVhZHMyJG1lYW4uY291bnQgPj0gMTApCm5hbWVzKHRtcCkgPC0gYygiZ2lkIiwiYXNlLmNvbnRyYXN0IiwiYXNlLkJXLnJhdGlvIiwiYXNlLkJXLmNvdW50IikKYncubWVyZ2UgPC0gbWVyZ2UocGxvdC5vdXQyLmJ3LHRtcCxieS54PSJmZWF0dXJlIixieS55PSJnaWQiLGFsbC54PVQpCnRtcDIgPC0gc3Vic2V0KGFzZS5yZWFkczIsYXNlLnJlYWRzMiRjb250cmFzdCA9PSAiV0IiICYgYXNlLnJlYWRzMiRtZWFuLmNvdW50ID49IDEwKQpuYW1lcyh0bXAyKSA8LSBjKCJnaWQiLCJhc2UuY29udHJhc3QiLCJhc2UuV0IucmF0aW8iLCJhc2UuV0IuY291bnQiKQpidy5tZXJnZTIgPC0gbWVyZ2UoYncubWVyZ2UsdG1wMixieS54PSJmZWF0dXJlIixieS55PSJnaWQiLGFsbC54PVQpCgpidy5tZXJnZTIkZ2Vub3R5cGUuYmlhc2VkIDwtIGlmZWxzZShidy5tZXJnZTIkYXNlLkJXLnJhdGlvID49IDAuOSAmIGJ3Lm1lcmdlMiRhc2UuV0IucmF0aW8gPD0gMC4xLCAiQjczLmJpYXNlZCIsaWZlbHNlKGJ3Lm1lcmdlMiRhc2UuV0IucmF0aW8gPj0gMC45ICYgYncubWVyZ2UyJGFzZS5CVy5yYXRpbyA8PSAwLjEsIlcyMi5iaWFzZWQiLCJmaW5lIikpCmJ3Lm1lcmdlMiRncm91cCA8LSBmYWN0b3IoaWZlbHNlKGJ3Lm1lcmdlMiRnZW5vdHlwZS5iaWFzZWQgPT0gImZpbmUiLGJ3Lm1lcmdlMiRmaWx0ZXIuaW1wcmludCxidy5tZXJnZTIkZ2Vub3R5cGUuYmlhc2VkKSxsZXZlbHMgPSBjKCJCNzMuYmlhc2VkIiwiVzIyLmJpYXNlZCIsIk1FRyIsIlBFRyIsIm5vLmltcHJpbnQiKSkKCmEgPC0gZ2dFeHRyYTo6Z2dNYXJnaW5hbChnZ3Bsb3QoYncubWVyZ2UyLGFlcyh4PWFzZS5CVy5yYXRpbyx5PW1hdGVybmFsX3ByZWZlcmVuY2UpKSArIHRoZW1lX2NsYXNzaWMoKSArIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gZ3JvdXAsYWxwaGEgPSAwLjUpKSArIHhsYWIoIlNOUC1BU0UgQnhXIikgKyB5bGFiKCJSRVIiKSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpICsgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoImRhcmtncmVlbiIsInB1cnBsZTQiLCJtYWdlbnRhIiwiYmx1ZSIsImdyYXkiKSksdHlwZT0iaGlzdG9ncmFtIikKCmIgPC0gZ2dFeHRyYTo6Z2dNYXJnaW5hbChnZ3Bsb3QoYncubWVyZ2UyLGFlcyh4PWFzZS5XQi5yYXRpbyx5PW1hdGVybmFsX3ByZWZlcmVuY2UpKSArIHRoZW1lX2NsYXNzaWMoKSArIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gZ3JvdXAsYWxwaGEgPSAwLjUpKSArIHhsYWIoIlNOUC1BU0UgV3hCIikgKyB5bGFiKCJSRVIiKSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpICsgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoImRhcmtncmVlbiIsInB1cnBsZTQiLCJtYWdlbnRhIiwiYmx1ZSIsImdyYXkiKSksdHlwZT0iaGlzdG9ncmFtIikKCmMgPC0gZ2dFeHRyYTo6Z2dNYXJnaW5hbChnZ3Bsb3QoYncubWVyZ2UyLGFlcyh4PWFzZS5CVy5yYXRpbyx5PWFzZS5XQi5yYXRpbykpICsgdGhlbWVfY2xhc3NpYygpICsgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBncm91cCxhbHBoYSA9IDAuNSkpICsgeGxhYigiU05QLUFTRSBXeEIiKSArIHlsYWIoIlNOUC1BU0UgQnhXIikgKyB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSArIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJkYXJrZ3JlZW4iLCJwdXJwbGU0IiwibWFnZW50YSIsImJsdWUiLCJncmF5IikpLHR5cGU9Imhpc3RvZ3JhbSIpCgpncmlkLmFycmFuZ2UoYSxiLGMsbnJvdz0yKQpgYGAKCgoKQjczIHZzIFcyMgpgYGB7cn0KYSA8LSBnZ3Bsb3QoYncubWVyZ2UyLGFlcyh4PWFzZS5CVy5yYXRpbyx5PW1hdGVybmFsX3ByZWZlcmVuY2UpKSArIGdlb21fYmluMmQoYmlucyA9IDUwKSArIHRoZW1lX2NsYXNzaWMoKSArIHNjYWxlX2ZpbGxfdmlyaWRpc19jKG9wdGlvbiA9ICJpbmZlcm5vIikgKyB4bGFiKCJTTlAtQVNFIEJ4VyIpICsgeWxhYigiUkVSIikKYiA8LSBnZ3Bsb3QoYncubWVyZ2UyLGFlcyh4PWFzZS5XQi5yYXRpbyx5PW1hdGVybmFsX3ByZWZlcmVuY2UpKSArIGdlb21fYmluMmQoYmlucyA9IDUwKSArIHRoZW1lX2NsYXNzaWMoKSArIHNjYWxlX2ZpbGxfdmlyaWRpc19jKG9wdGlvbiA9ICJpbmZlcm5vIikgKyB4bGFiKCJTTlAtQVNFIFd4QiIpICsgeWxhYigiUkVSIikKCmdyaWQuYXJyYW5nZShhLGIsbnJvdz0xKQpgYGAKQ29tcGFyZSBCNzMgdnMgUEgyMDcKYGBge3J9CmJwLm1lcmdlZC5vdXQgPC0gbWVyZ2UocGxvdC5vdXQyLmJwLHN1YnNldChhc2UucmVhZHMyLGFzZS5yZWFkczIkY29udHJhc3QgPT0gIkJQIiksYnkueD0iZmVhdHVyZSIsYnkueT0iZ2lkIixhbGw9RikKYnAubWVyZ2VkLm91dDIgPC0gc3Vic2V0KGJwLm1lcmdlZC5vdXQsYnAubWVyZ2VkLm91dCRtZWFuLmNvdW50ID49IDEwKQphIDwtIGdncGxvdChicC5tZXJnZWQub3V0MixhZXMoeD1tZWFuLnJhdGlvLHk9bWF0ZXJuYWxfcHJlZmVyZW5jZSkpICsgZ2VvbV9iaW4yZChiaW5zID0gNTApICsgdGhlbWVfY2xhc3NpYygpICsgc2NhbGVfZmlsbF92aXJpZGlzX2Mob3B0aW9uID0gImluZmVybm8iKSArIHhsYWIoIk1lYW4gU05QIE1hdGVybmFsIFJhdGlvIEJ4VyIpICsgeWxhYigiTWF0ZXJuYWwgUHJlZmVyZW5jZSBSYXRpbyIpCgpicC5tZXJnZWQub3V0MyA8LSBtZXJnZShwbG90Lm91dDIuYnAsc3Vic2V0KGFzZS5yZWFkczIsYXNlLnJlYWRzMiRjb250cmFzdCA9PSAiUEIiKSxieS54PSJmZWF0dXJlIixieS55PSJnaWQiLGFsbD1GKQpicC5tZXJnZWQub3V0NCA8LSBzdWJzZXQoYnAubWVyZ2VkLm91dDMsYnAubWVyZ2VkLm91dDMkbWVhbi5jb3VudCA+PSAxMCkKCmIgPC0gZ2dwbG90KGJwLm1lcmdlZC5vdXQ0LGFlcyh4PW1lYW4ucmF0aW8seT1tYXRlcm5hbF9wcmVmZXJlbmNlKSkgKyBnZW9tX2JpbjJkKGJpbnMgPSA1MCkgKyB0aGVtZV9jbGFzc2ljKCkgKyBzY2FsZV9maWxsX3ZpcmlkaXNfYyhvcHRpb24gPSAiaW5mZXJubyIpICsgeGxhYigiTWVhbiBTTlAgTWF0ZXJuYWwgUmF0aW8gV3hCIikgKyB5bGFiKCJNYXRlcm5hbCBQcmVmZXJlbmNlIFJhdGlvIikKCmdyaWQuYXJyYW5nZShhLGIsbnJvdz0xKQpgYGAKCgoKCgpQbG90IGp1c3QgZ2VuZXMKYGBge3J9CnBsb3QuanVzdC5nZW5lcyA8LSByYmluZChzdWJzZXQocGxvdC5vdXQyLmJ3LHBsb3Qub3V0Mi5idyRmZWF0dXJlX3R5cGUgPT0gImdlbmUiKSxzdWJzZXQocGxvdC5vdXQyLmJwLHBsb3Qub3V0Mi5icCRmZWF0dXJlX3R5cGUgPT0gImdlbmUiKSxzdWJzZXQocGxvdC5vdXQyLndwLHBsb3Qub3V0Mi53cCRmZWF0dXJlX3R5cGUgPT0gImdlbmUiKSkKCmdncGxvdChwbG90Lmp1c3QuZ2VuZXMsYWVzKHg9aW1wcmludCx5PW1hdGVybmFsX3ByZWZlcmVuY2UsZmlsbD1pbXByaW50KSkgKyBnZW9tX3Zpb2xpbigpICsgdGhlbWVfY2xhc3NpYygpICsgdGhlbWUocGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSxwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpKSArIGZhY2V0X2dyaWQoZ2Vub21lfmNvbnRyYXN0KSArIGdlb21fYWJsaW5lKHNsb3BlPTAsIGludGVyY2VwdD0uMzMpICsgZ2VvbV9hYmxpbmUoc2xvcGU9MCwgaW50ZXJjZXB0PS42NikgKyBzdGF0X3N1bW1hcnkoZnVuLmRhdGEgPSBnaXZlLm4sIGdlb20gPSAidGV4dCIpIAoKdGFibGUocGxvdC5qdXN0LmdlbmVzWyxjKCJnZW5vbWUiLCJjb250cmFzdCIsImltcHJpbnQiKV0pCmBgYAoKYGBge3J9Cm1lZy5nZW5lcyA8LSBzdWJzZXQoYi5ib3RoLmNvbnRyYXN0cyxiLmJvdGguY29udHJhc3RzJGltcHJpbnQgPT0gIk1FRyIpCmxlbmd0aChtZWcuZ2VuZXMkZmVhdHVyZVtkdXBsaWNhdGVkKG1lZy5nZW5lcyRmZWF0dXJlKV0pCmxlbmd0aCh1bmlxdWUobWVnLmdlbmVzJGZlYXR1cmUpKQpgYGAKYGBge3J9CnBlZy5nZW5lcyA8LSBzdWJzZXQoYi5ib3RoLmNvbnRyYXN0cyxiLmJvdGguY29udHJhc3RzJGltcHJpbnQgPT0gIlBFRyIpCmxlbmd0aChwZWcuZ2VuZXMkZmVhdHVyZVtkdXBsaWNhdGVkKHBlZy5nZW5lcyRmZWF0dXJlKV0pCmxlbmd0aCh1bmlxdWUocGVnLmdlbmVzJGZlYXR1cmUpKQpgYGAKYGBge3J9Cm1lZy50ZXMgPC0gc3Vic2V0KGIuYm90aC5jb250cmFzdHMsYi5ib3RoLmNvbnRyYXN0cyRpbXByaW50ID09ICJtYXRURSIpCmxlbmd0aChtZWcudGVzJGZlYXR1cmVbZHVwbGljYXRlZChtZWcudGVzJGZlYXR1cmUpXSkKbGVuZ3RoKHVuaXF1ZShtZWcudGVzJGZlYXR1cmUpKQpgYGAKCkNoZWNrIGVuZG9zcGVybSB2cyBwZXJpY2FycCBleHByZXNzaW9uLCBlc3BlY2lhbGx5IGZvciBNRUdzCmBgYHtyfQp1bmZpbHRlcmVkLm1lZy5nZW5lcyA8LSBzdWJzZXQoYi5ib3RoLmNvbnRyYXN0cyxiLmJvdGguY29udHJhc3RzJGZlYXR1cmVfdHlwZSA9PSAiZ2VuZSIgJiAoYi5ib3RoLmNvbnRyYXN0cyRmaWx0ZXIuaW1wcmludC5CVyA9PSAiTUVHIiB8IGIuYm90aC5jb250cmFzdHMkZmlsdGVyLmltcHJpbnQuQlcgPT0gImZpbHRlcmVkLk1FRyIpKQoKaW1wcmludGVkLm1lZy40aGVhdCA8LSBhcy5tYXRyaXgoc3Vic2V0KGRldi5ycG0sIHJvd25hbWVzKGRldi5ycG0pICVpbiUgdW5maWx0ZXJlZC5tZWcuZ2VuZXMkZmVhdHVyZSAmIHJvd01heHMoYXMubWF0cml4KGRldi5ycG1bLGtlZXAuY29sdW1uc10pKSA+IDApWyxrZWVwLmNvbHVtbnNdKQpwaGVhdG1hcCgoaW1wcmludGVkLm1lZy40aGVhdC9yb3dNYXhzKGltcHJpbnRlZC5tZWcuNGhlYXQpKSxib3JkZXJfY29sb3IgPSBOQSxmb250c2l6ZSA9IDgsY2x1c3Rlcl9jb2xzID0gRikKYGBgCmBgYHtyfQphIDwtIHN1YnNldChpbXByaW50ZWQubWVnLjRoZWF0LHJvd25hbWVzKGltcHJpbnRlZC5tZWcuNGhlYXQpICVpbiUgcm93bmFtZXMocGVyaS5wcmVmZXJyZWQpKS9yb3dNYXhzKHN1YnNldChpbXByaW50ZWQubWVnLjRoZWF0LHJvd25hbWVzKGltcHJpbnRlZC5tZWcuNGhlYXQpICVpbiUgcm93bmFtZXMocGVyaS5wcmVmZXJyZWQpKSkKYiA8LSBzdWJzZXQoaW1wcmludGVkLm1lZy40aGVhdCwhcm93bmFtZXMoaW1wcmludGVkLm1lZy40aGVhdCkgJWluJSByb3duYW1lcyhwZXJpLnByZWZlcnJlZCkpL3Jvd01heHMoc3Vic2V0KGltcHJpbnRlZC5tZWcuNGhlYXQsIXJvd25hbWVzKGltcHJpbnRlZC5tZWcuNGhlYXQpICVpbiUgcm93bmFtZXMocGVyaS5wcmVmZXJyZWQpKSkKaW1wcmludGVkLm1lZy40aGVhdC52MiA8LSByYmluZChhW29yZGVyKHJvd01lYW5zKGEpLGRlY3JlYXNpbmcgPSBUKSxdLGJbb3JkZXIocm93TWVhbnMoYiksZGVjcmVhc2luZyA9IFQpLF0pCnBoZWF0bWFwKChpbXByaW50ZWQubWVnLjRoZWF0LnYyL3Jvd01heHMoaW1wcmludGVkLm1lZy40aGVhdC52MikpLGJvcmRlcl9jb2xvciA9IE5BLGZvbnRzaXplID0gOCxjbHVzdGVyX2NvbHMgPSBGLGNsdXN0ZXJfcm93cyA9IEYsc2hvd19yb3duYW1lcyA9IEYsZ2Fwc19yb3cgPSBucm93KGEpKQpgYGAKCgpDaGVjayBmb3IgVEVzCmBgYHtyfQpwZXJpLnRlLmhlYXQgPC0gYXMubWF0cml4KHN1YnNldChkZXYucnBtLCByb3duYW1lcyhkZXYucnBtKSAlaW4lIG1lZy50ZXMkZmVhdHVyZSAmIHJvd01heHMoYXMubWF0cml4KGRldi5ycG1bLGtlZXAuY29sdW1uc10pKSA+IDApWyxrZWVwLmNvbHVtbnNdKQpwaGVhdG1hcCgocGVyaS50ZS5oZWF0L3Jvd01heHMocGVyaS50ZS5oZWF0KSksYm9yZGVyX2NvbG9yID0gTkEsZm9udHNpemUgPSA4LGNsdXN0ZXJfY29scyA9IEYpCmBgYApgYGB7cn0KcGVyaS50ZS5oZWF0LnYyIDwtIHBlcmkudGUuaGVhdC9yb3dNYXhzKHBlcmkudGUuaGVhdCkKcGhlYXRtYXAoKHBlcmkudGUuaGVhdC52MltvcmRlcihyb3dNZWFucyhwZXJpLnRlLmhlYXQudjIpLGRlY3JlYXNpbmcgPSBUKSxdKSxib3JkZXJfY29sb3IgPSBOQSxmb250c2l6ZSA9IDgsY2x1c3Rlcl9jb2xzID0gRixjbHVzdGVyX3Jvd3MgPSBGLHNob3dfcm93bmFtZXMgPSBGKQpgYGAKClBsb3QganVzdCBURXMKYGBge3J9CnBsb3QuanVzdC50ZXMgPC0gcmJpbmQoc3Vic2V0KHBsb3Qub3V0Mi5idyxwbG90Lm91dDIuYnckZmVhdHVyZV90eXBlID09ICJURSIpLHN1YnNldChwbG90Lm91dDIuYnAscGxvdC5vdXQyLmJwJGZlYXR1cmVfdHlwZSA9PSAiVEUiKSxzdWJzZXQocGxvdC5vdXQyLndwLHBsb3Qub3V0Mi53cCRmZWF0dXJlX3R5cGUgPT0gIlRFIikpCgpnZ3Bsb3QocGxvdC5qdXN0LnRlcyxhZXMoeD1pbXByaW50LHk9bWF0ZXJuYWxfcHJlZmVyZW5jZSxmaWxsPWltcHJpbnQpKSArIGdlb21fdmlvbGluKCkgKyB0aGVtZV9jbGFzc2ljKCkgKyB0aGVtZShwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCkpICsgZmFjZXRfZ3JpZChnZW5vbWV+Y29udHJhc3QpICsgZ2VvbV9hYmxpbmUoc2xvcGU9MCwgaW50ZXJjZXB0PS4zMykgKyBnZW9tX2FibGluZShzbG9wZT0wLCBpbnRlcmNlcHQ9LjY2KSArIHN0YXRfc3VtbWFyeShmdW4uZGF0YSA9IGdpdmUubiwgZ2VvbSA9ICJ0ZXh0IikgCgp0YWJsZShwbG90Lmp1c3QudGVzWyxjKCJnZW5vbWUiLCJjb250cmFzdCIsImZpbHRlci5pbXByaW50IildKQpgYGAKCgoKTWFrZSBkaXN0cmlidXRpb24gb2YgYWxsIGJhc2VkIG9uIHdoaWNoIGxpYnJhcmllcyBnb3QgaW5jbHVkZWQgaW4gREUgY2FsbHMKYGBge3J9CmltcC5yZWNpcHMucnBtMiA8LSBkYXRhLmZyYW1lKHJiaW5kKHN1YnNldChpbXAuQnZXLnJwbSxpbXAuQnZXLnJwbSRmZWF0dXJlICVpbiUgcm93bmFtZXMoaW1wLmFsbC5kZS5rZWVwLmJ3KSksc3Vic2V0KGltcC5CdlAucnBtLGltcC5CdlAucnBtJGZlYXR1cmUgJWluJSByb3duYW1lcyhpbXAuYWxsLmRlLmtlZXAuYnApKSxzdWJzZXQoaW1wLld2UC5ycG0saW1wLld2UC5ycG0kZmVhdHVyZSAlaW4lIHJvd25hbWVzKGltcC5hbGwuZGUua2VlcC53cCkpKSkKCmltcC5yZWNpcHMucnBtMiRBX21lYW4gPC0gcm93TWVhbnMoaW1wLnJlY2lwcy5ycG0yWywxOjNdKQppbXAucmVjaXBzLnJwbTIkQl9tZWFuIDwtIHJvd01lYW5zKGltcC5yZWNpcHMucnBtMlssNDo2XSkKaW1wLnJlY2lwcy5ycG0yJG1hdGVybmFsX3ByZWZlcmVuY2UgPC0gaWZlbHNlKGltcC5yZWNpcHMucnBtMiR0eXBlID09ICJBIixpbXAucmVjaXBzLnJwbTIkQV9tZWFuLyhpbXAucmVjaXBzLnJwbTIkQV9tZWFuICsgaW1wLnJlY2lwcy5ycG0yJEJfbWVhbiksaW1wLnJlY2lwcy5ycG0yJEJfbWVhbi8oaW1wLnJlY2lwcy5ycG0yJEFfbWVhbiArIGltcC5yZWNpcHMucnBtMiRCX21lYW4pKSAjIG1hdGVybmFsIHByZWZlcmFuY2UgY2FsY3VsYXRpb24KCmdncGxvdChpbXAucmVjaXBzLnJwbTIsYWVzKHg9Z2Vub21lLHk9bWF0ZXJuYWxfcHJlZmVyZW5jZSxmaWxsPWdlbm9tZSkpICsgZ2VvbV9hYmxpbmUoc2xvcGU9MCwgaW50ZXJjZXB0PS4zMykgKyBnZW9tX2FibGluZShzbG9wZT0wLCBpbnRlcmNlcHQ9LjY2KSArIGdlb21fdmlvbGluKCkgKyB0aGVtZV9jbGFzc2ljKCkgKyB0aGVtZShwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCkpICsgZmFjZXRfd3JhcChmZWF0dXJlX3R5cGV+Y29udHJhc3Qsc2NhbGVzPSJmcmVlX3giKSArIHN0YXRfc3VtbWFyeShmdW4uZGF0YSA9IGdpdmUubiwgZ2VvbSA9ICJ0ZXh0IikgCmBgYApQbG90IGFzIHN0YWNrZWQgYmFycyBpbnN0ZWFkCmBgYHtyfQppbXAucmVjaXBzLnJwbTIkbWF0LmNhdGVnb3J5IDwtIGZhY3RvcihpZmVsc2UoaW1wLnJlY2lwcy5ycG0yJG1hdGVybmFsX3ByZWZlcmVuY2UgPiAwLjksICJzdHJvbmcubWF0ZXJuYWwiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoaW1wLnJlY2lwcy5ycG0yJG1hdGVybmFsX3ByZWZlcmVuY2UgPCAwLjEsICJzdHJvbmcucGF0ZXJuYWwiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKGltcC5yZWNpcHMucnBtMiRtYXRlcm5hbF9wcmVmZXJlbmNlID4gMC44LCAibW9kZXJhdGUubWF0ZXJuYWwiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShpbXAucmVjaXBzLnJwbTIkbWF0ZXJuYWxfcHJlZmVyZW5jZSA8IDAuMiwgIm1vZGVyYXRlLnBhdGVybmFsIiwgImJpcGFyZW50YWwiKSkpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBjKCJzdHJvbmcubWF0ZXJuYWwiLCJtb2RlcmF0ZS5tYXRlcm5hbCIsImJpcGFyZW50YWwiLCJtb2RlcmF0ZS5wYXRlcm5hbCIsInN0cm9uZy5wYXRlcm5hbCIpKQppbXAucmVjaXBzLnJwbTIkZmVhdHVyZV9jYXRlZ29yeSA8LSBmYWN0b3IoaWZlbHNlKGltcC5yZWNpcHMucnBtMiRmZWF0dXJlX3R5cGUgPT0gIlRFIiwiVEUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKGltcC5yZWNpcHMucnBtMiRmZWF0dXJlICVpbiUgZ2VuZS5rZXkuQldQJHY0X2dlbmVfbW9kZWwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBpbXAucmVjaXBzLnJwbTIkZmVhdHVyZSAlaW4lIGdlbmUua2V5LkJXUCRXMjIuWm0wMDAwNGIuMS4gCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBpbXAucmVjaXBzLnJwbTIkZmVhdHVyZSAlaW4lIGdlbmUua2V5LkJXUCRQSDIwNy5abTAwMDA4YS4xLiwgIm1haXplLnN5bnRlbmljLmdlbmUiLCJtYWl6ZS5ub25zeW50ZW5pYy5nZW5lIikpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzPWMoIm1haXplLnN5bnRlbmljLmdlbmUiLCJtYWl6ZS5ub25zeW50ZW5pYy5nZW5lIiwiVEUiKSkKc3RhY2subWF0LmNhdGVnb3J5IDwtIGRhdGEuZnJhbWUoaW1wLnJlY2lwcy5ycG0yICU+JSBncm91cF9ieShmZWF0dXJlX2NhdGVnb3J5LGdlbm9tZSxjb250cmFzdCxtYXQuY2F0ZWdvcnkpICU+JSBkcGx5cjo6c3VtbWFyaXNlKG4gPSBuKCkpICU+JSBkcGx5cjo6bXV0YXRlKHByb3BvcnRpb24gPSBuL3N1bShuKSkpCnN0YWNrLm1hdC5jYXRlZ29yeSRzYW1wbGUgPC0gcGFzdGUoc3RhY2subWF0LmNhdGVnb3J5JGdlbm9tZSxzdGFjay5tYXQuY2F0ZWdvcnkkY29udHJhc3Qsc2VwPSJfIikKZ2dwbG90KCkgKyBnZW9tX2JhcihhZXMoeD1zYW1wbGUseT1wcm9wb3J0aW9uLGZpbGw9bWF0LmNhdGVnb3J5KSxkYXRhPXN0YWNrLm1hdC5jYXRlZ29yeSxzdGF0PSJpZGVudGl0eSIscG9zaXRpb24gPSBwb3NpdGlvbl9maWxsKCkpICsgdGhlbWVfY2xhc3NpYygpICsgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWMoIm1hZ2VudGEiLCJtYWdlbnRhNCIsImRhcmtncmF5IiwibmF2eSIsImJsdWUiKSkgKyBmYWNldF93cmFwKC5+ZmVhdHVyZV9jYXRlZ29yeSxzY2FsZXM9ImZyZWUiKSArIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlPTkwLCBoanVzdD0xKSkgCmBgYApwcm9wb3J0aW9uLnN0cm9uZyAtIHN5bnRlbmljIGdlbmVzCmBgYHtyfQp0bXAgPC0gc3Vic2V0KHN0YWNrLm1hdC5jYXRlZ29yeSxzdGFjay5tYXQuY2F0ZWdvcnkkZmVhdHVyZV9jYXRlZ29yeSA9PSAibWFpemUuc3ludGVuaWMuZ2VuZSIgJiBncmVwbCgic3Ryb25nIixzdGFjay5tYXQuY2F0ZWdvcnkkbWF0LmNhdGVnb3J5KSkKdG1wMiA8LSBkYXRhLmZyYW1lKHRtcCAlPiUgZ3JvdXBfYnkoc2FtcGxlKSAlPiUgZHBseXI6OnN1bW1hcmlzZShzdHJvbmcucGFyZW50YWwgPSBzdW0ocHJvcG9ydGlvbikpKQptZWFuKHRtcDIkc3Ryb25nLnBhcmVudGFsKQpgYGAKVEVzCmBgYHtyfQp0bXAgPC0gc3Vic2V0KHN0YWNrLm1hdC5jYXRlZ29yeSxzdGFjay5tYXQuY2F0ZWdvcnkkZmVhdHVyZV9jYXRlZ29yeSA9PSAiVEUiICYgZ3JlcGwoInN0cm9uZyIsc3RhY2subWF0LmNhdGVnb3J5JG1hdC5jYXRlZ29yeSkpCnRtcDIgPC0gZGF0YS5mcmFtZSh0bXAgJT4lIGdyb3VwX2J5KHNhbXBsZSkgJT4lIGRwbHlyOjpzdW1tYXJpc2Uoc3Ryb25nLnBhcmVudGFsID0gc3VtKHByb3BvcnRpb24pKSkKbWVhbih0bXAyJHN0cm9uZy5wYXJlbnRhbCkKYGBgCm5vbnN5bnRlbmljIGdlbmVzCmBgYHtyfQp0bXAgPC0gc3Vic2V0KHN0YWNrLm1hdC5jYXRlZ29yeSxzdGFjay5tYXQuY2F0ZWdvcnkkZmVhdHVyZV9jYXRlZ29yeSA9PSAibWFpemUubm9uc3ludGVuaWMuZ2VuZSIgJiBncmVwbCgic3Ryb25nIixzdGFjay5tYXQuY2F0ZWdvcnkkbWF0LmNhdGVnb3J5KSkKdG1wMiA8LSBkYXRhLmZyYW1lKHRtcCAlPiUgZ3JvdXBfYnkoc2FtcGxlKSAlPiUgZHBseXI6OnN1bW1hcmlzZShzdHJvbmcucGFyZW50YWwgPSBzdW0ocHJvcG9ydGlvbikpKQptZWFuKHRtcDIkc3Ryb25nLnBhcmVudGFsKQpgYGAKCmBgYHtyfQpnZ3Bsb3QoaW1wLnJlY2lwcy5ycG0yLGFlcyh4PWdlbm9tZSx5PW1hdGVybmFsX3ByZWZlcmVuY2UsZmlsbD1nZW5vbWUpKSArIGdlb21fYWJsaW5lKHNsb3BlPTAsIGludGVyY2VwdD0uMzMpICsgZ2VvbV9hYmxpbmUoc2xvcGU9MCwgaW50ZXJjZXB0PS42NikgKyBnZW9tX3Zpb2xpbigpICsgdGhlbWVfY2xhc3NpYygpICsgdGhlbWUocGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSxwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpKSArIGZhY2V0X3dyYXAoZmVhdHVyZV9jYXRlZ29yeX5jb250cmFzdCxzY2FsZXM9ImZyZWVfeCIpICsgc3RhdF9zdW1tYXJ5KGZ1bi5kYXRhID0gZ2l2ZS5uLCBnZW9tID0gInRleHQiKSAKYGBgCgoKYGBge3J9CmdlbmUua2V5LkJXUCRCLmltcC5CVyA8LSBpZmVsc2UoZ2VuZS5rZXkuQldQJHY0X2dlbmVfbW9kZWwgJWluJSBzdWJzZXQocGxvdC5vdXQyLmJ3LHBsb3Qub3V0Mi5idyRjYXRlZ29yeSAhPSAibm90REUiKVssImZlYXR1cmUiXSwiaW1wcmludGVkIixpZmVsc2UoZ2VuZS5rZXkuQldQJHY0X2dlbmVfbW9kZWwgJWluJSBwbG90Lm91dDIuYnckZmVhdHVyZSwibm90LmltcHJpbnRlZCIsIk5BIikpCmdlbmUua2V5LkJXUCRXLmltcC5CVyA8LSBpZmVsc2UoZ2VuZS5rZXkuQldQJFcyMi5abTAwMDA0Yi4xLiAlaW4lIHN1YnNldChwbG90Lm91dDIuYncscGxvdC5vdXQyLmJ3JGNhdGVnb3J5ICE9ICJub3RERSIpWywiZmVhdHVyZSJdLCJpbXByaW50ZWQiLGlmZWxzZShnZW5lLmtleS5CV1AkVzIyLlptMDAwMDRiLjEuICVpbiUgcGxvdC5vdXQyLmJ3JGZlYXR1cmUsIm5vdC5pbXByaW50ZWQiLCJOQSIpKQoKZ2VuZS5rZXkuQldQJEIuaW1wLkJQIDwtIGlmZWxzZShnZW5lLmtleS5CV1AkdjRfZ2VuZV9tb2RlbCAlaW4lIHN1YnNldChwbG90Lm91dDIuYnAscGxvdC5vdXQyLmJwJGNhdGVnb3J5ICE9ICJub3RERSIpWywiZmVhdHVyZSJdLCJpbXByaW50ZWQiLGlmZWxzZShnZW5lLmtleS5CV1AkdjRfZ2VuZV9tb2RlbCAlaW4lIHBsb3Qub3V0Mi5icCRmZWF0dXJlLCJub3QuaW1wcmludGVkIiwiTkEiKSkKZ2VuZS5rZXkuQldQJFAuaW1wLkJQIDwtIGlmZWxzZShnZW5lLmtleS5CV1AkUEgyMDcuWm0wMDAwOGEuMS4gJWluJSBzdWJzZXQocGxvdC5vdXQyLmJwLHBsb3Qub3V0Mi5icCRjYXRlZ29yeSAhPSAibm90REUiKVssImZlYXR1cmUiXSwiaW1wcmludGVkIixpZmVsc2UoZ2VuZS5rZXkuQldQJFBIMjA3LlptMDAwMDhhLjEuICVpbiUgcGxvdC5vdXQyLmJwJGZlYXR1cmUsIm5vdC5pbXByaW50ZWQiLCJOQSIpKQoKZ2VuZS5rZXkuQldQJFAuaW1wLldQIDwtIGlmZWxzZShnZW5lLmtleS5CV1AkUEgyMDcuWm0wMDAwOGEuMS4gJWluJSBzdWJzZXQocGxvdC5vdXQyLndwLHBsb3Qub3V0Mi53cCRjYXRlZ29yeSAhPSAibm90REUiKVssImZlYXR1cmUiXSwiaW1wcmludGVkIixpZmVsc2UoZ2VuZS5rZXkuQldQJFBIMjA3LlptMDAwMDhhLjEuICVpbiUgcGxvdC5vdXQyLndwJGZlYXR1cmUsIm5vdC5pbXByaW50ZWQiLCJOQSIpKQpnZW5lLmtleS5CV1AkVy5pbXAuV1AgPC0gaWZlbHNlKGdlbmUua2V5LkJXUCRXMjIuWm0wMDAwNGIuMS4gJWluJSBzdWJzZXQocGxvdC5vdXQyLndwLHBsb3Qub3V0Mi53cCRjYXRlZ29yeSAhPSAibm90REUiKVssImZlYXR1cmUiXSwiaW1wcmludGVkIixpZmVsc2UoZ2VuZS5rZXkuQldQJFcyMi5abTAwMDA0Yi4xLiAlaW4lIHBsb3Qub3V0Mi53cCRmZWF0dXJlLCJub3QuaW1wcmludGVkIiwiTkEiKSkKYGBgCgpMb29rIGF0IGVuZG9zcGVybS1zcGVjaWZpYyBleHByZXNzaW9uIHZzIGNvbnN0aXR1YXRpdmUgZXhwcmVzc2lvbgpgYGB7cn0KZW5kby5zZWVkLmNvbHVtbnMgPC0gYyhuYW1lcyhkZXYucnBtKVtncmVwKCJlbmRvc3Blcm0iLG5hbWVzKGRldi5ycG0pKV0sbmFtZXMoZGV2LnJwbSlbZ3JlcCgic2VlZCIsbmFtZXMoZGV2LnJwbSkpXSkKb3RoZXIuY29sdW1ucyA8LSBuYW1lcyhkZXYucnBtKVshbmFtZXMoZGV2LnJwbSkgJWluJSBlbmRvLnNlZWQuY29sdW1uc10Kb3RoZXIuY29sdW1ucyA8LSBvdGhlci5jb2x1bW5zW29yZGVyKG90aGVyLmNvbHVtbnMpXQplbmRvLnByZWZlcnJlZCA8LSByb3duYW1lcyhzdWJzZXQoZGV2LnJwbSxyb3dTdW1zKGRldi5ycG1bLGVuZG8uc2VlZC5jb2x1bW5zXSkvcm93U3VtcyhkZXYucnBtKSA+IDAuNikpCnBsb3QuY29sdW1uLm9yZGVyIDwtIGMoZW5kby5zZWVkLmNvbHVtbnMsb3RoZXIuY29sdW1ucykKYGBgCgpTdW1tYXJpemUgZmVhdHVyZXMgZnVydGhlcgpgYGB7cn0KYi5ib3RoLmNvbnRyYXN0cyRleHByZXNzaW9uLnR5cGUgPC0gaWZlbHNlKGIuYm90aC5jb250cmFzdHMkZmVhdHVyZSAlaW4lIGVuZG8ucHJlZmVycmVkLCAiZW5kby5wcmVmZXJyZWQiLCJjb25zdGl0dXRpdmUiKQpgYGAKCk1FRyBnZW5lcwpgYGB7cn0KaW1wcmludGVkLm1lZy40aGVhdCA8LSBhcy5tYXRyaXgoc3Vic2V0KGRldi5ycG0sIHJvd25hbWVzKGRldi5ycG0pICVpbiUgbWVnLmdlbmVzJGZlYXR1cmUpKQoKYSA8LSBzdWJzZXQoaW1wcmludGVkLm1lZy40aGVhdCxyb3duYW1lcyhpbXByaW50ZWQubWVnLjRoZWF0KSAlaW4lIGVuZG8ucHJlZmVycmVkKQpiIDwtIHN1YnNldChpbXByaW50ZWQubWVnLjRoZWF0LCFyb3duYW1lcyhpbXByaW50ZWQubWVnLjRoZWF0KSAlaW4lIGVuZG8ucHJlZmVycmVkKQpjIDwtIHJiaW5kKGEsYikKCnBoZWF0bWFwKChhL3Jvd01heHMoYSkpWyxwbG90LmNvbHVtbi5vcmRlcl0sYm9yZGVyX2NvbG9yID0gTkEsZm9udHNpemUgPSA4LGNsdXN0ZXJfY29scyA9IEYsY2x1c3RlcmluZ19kaXN0YW5jZV9yb3dzID0gImV1Y2xpZGVhbiIpCnBoZWF0bWFwKChiL3Jvd01heHMoYikpWyxwbG90LmNvbHVtbi5vcmRlcl0sYm9yZGVyX2NvbG9yID0gTkEsZm9udHNpemUgPSA4LGNsdXN0ZXJfY29scyA9IEYsY2x1c3RlcmluZ19kaXN0YW5jZV9yb3dzID0gImV1Y2xpZGVhbiIpCnBoZWF0bWFwKChjL3Jvd01heHMoYykpWyxwbG90LmNvbHVtbi5vcmRlcl0sYm9yZGVyX2NvbG9yID0gTkEsZm9udHNpemUgPSA4LGNsdXN0ZXJfY29scyA9IEYsY2x1c3Rlcl9yb3dzID0gRixnYXBzX2NvbCA9IGxlbmd0aChlbmRvLnNlZWQuY29sdW1ucyksZ2Fwc19yb3cgPSBucm93KGEpLHNob3dfcm93bmFtZXMgPSBGLHNob3dfY29sbmFtZXMgPSBGLCBtYWluID0gIkRldmVsb3BtZW50YWwgRXhwcmVzc2lvbiBvZiBNRUdzIikKbWVnLmRldiA8LSBwaGVhdG1hcCgoYy9yb3dNYXhzKGMpKVsscGxvdC5jb2x1bW4ub3JkZXJdLGJvcmRlcl9jb2xvciA9IE5BLGZvbnRzaXplID0gOCxjbHVzdGVyX2NvbHMgPSBGLGNsdXN0ZXJfcm93cyA9IEYsZ2Fwc19jb2wgPSBsZW5ndGgoZW5kby5zZWVkLmNvbHVtbnMpLGdhcHNfcm93ID0gbnJvdyhhKSxzaG93X3Jvd25hbWVzID0gRixzaG93X2NvbG5hbWVzID0gRiwgbWFpbiA9ICJEZXZlbG9wbWVudGFsIEV4cHJlc3Npb24gb2YgTUVHcyIpCmBgYAoKUEVHIGdlbmVzCmBgYHtyfQppbXByaW50ZWQucGVnLjRoZWF0IDwtIGFzLm1hdHJpeChzdWJzZXQoZGV2LnJwbSwgcm93bmFtZXMoZGV2LnJwbSkgJWluJSBwZWcuZ2VuZXMkZmVhdHVyZSkpCgphIDwtIHN1YnNldChpbXByaW50ZWQucGVnLjRoZWF0LHJvd25hbWVzKGltcHJpbnRlZC5wZWcuNGhlYXQpICVpbiUgZW5kby5wcmVmZXJyZWQpCmIgPC0gc3Vic2V0KGltcHJpbnRlZC5wZWcuNGhlYXQsIXJvd25hbWVzKGltcHJpbnRlZC5wZWcuNGhlYXQpICVpbiUgZW5kby5wcmVmZXJyZWQpCmMgPC0gcmJpbmQoYSxiKQoKcGhlYXRtYXAoKGEvcm93TWF4cyhhKSlbLHBsb3QuY29sdW1uLm9yZGVyXSxib3JkZXJfY29sb3IgPSBOQSxmb250c2l6ZSA9IDgsY2x1c3Rlcl9jb2xzID0gRixjbHVzdGVyaW5nX2Rpc3RhbmNlX3Jvd3MgPSAiZXVjbGlkZWFuIikKcGhlYXRtYXAoKGIvcm93TWF4cyhiKSlbLHBsb3QuY29sdW1uLm9yZGVyXSxib3JkZXJfY29sb3IgPSBOQSxmb250c2l6ZSA9IDgsY2x1c3Rlcl9jb2xzID0gRixjbHVzdGVyaW5nX2Rpc3RhbmNlX3Jvd3MgPSAiZXVjbGlkZWFuIikKcGhlYXRtYXAoKGMvcm93TWF4cyhjKSlbLHBsb3QuY29sdW1uLm9yZGVyXSxib3JkZXJfY29sb3IgPSBOQSxmb250c2l6ZSA9IDgsY2x1c3Rlcl9jb2xzID0gRixjbHVzdGVyX3Jvd3MgPSBGLGdhcHNfY29sID0gbGVuZ3RoKGVuZG8uc2VlZC5jb2x1bW5zKSxnYXBzX3JvdyA9IG5yb3coYSksc2hvd19yb3duYW1lcyA9IEYsc2hvd19jb2xuYW1lcyA9IEYsIG1haW4gPSAiRGV2ZWxvcG1lbnRhbCBFeHByZXNzaW9uIG9mIFBFR3MiKQpwZWcuZGV2IDwtIHBoZWF0bWFwKChjL3Jvd01heHMoYykpWyxwbG90LmNvbHVtbi5vcmRlcl0sYm9yZGVyX2NvbG9yID0gTkEsZm9udHNpemUgPSA4LGNsdXN0ZXJfY29scyA9IEYsY2x1c3Rlcl9yb3dzID0gRixnYXBzX2NvbCA9IGxlbmd0aChlbmRvLnNlZWQuY29sdW1ucyksZ2Fwc19yb3cgPSBucm93KGEpLHNob3dfcm93bmFtZXMgPSBGLHNob3dfY29sbmFtZXMgPSBGLCBtYWluID0gIkRldmVsb3BtZW50YWwgRXhwcmVzc2lvbiBvZiBQRUdzIikKYGBgCm1hdGVybmFsIFRFcwpgYGB7cn0KaW1wcmludGVkLnRlLjRoZWF0IDwtIGFzLm1hdHJpeChzdWJzZXQoZGV2LnJwbSwgcm93bmFtZXMoZGV2LnJwbSkgJWluJSBtZWcudGVzJGZlYXR1cmUpKQoKYSA8LSBzdWJzZXQoaW1wcmludGVkLnRlLjRoZWF0LHJvd25hbWVzKGltcHJpbnRlZC50ZS40aGVhdCkgJWluJSBlbmRvLnByZWZlcnJlZCkKYiA8LSBzdWJzZXQoaW1wcmludGVkLnRlLjRoZWF0LCFyb3duYW1lcyhpbXByaW50ZWQudGUuNGhlYXQpICVpbiUgZW5kby5wcmVmZXJyZWQpCmMgPC0gcmJpbmQoYSxiKQoKcGhlYXRtYXAoKGEvcm93TWF4cyhhKSlbLHBsb3QuY29sdW1uLm9yZGVyXSxib3JkZXJfY29sb3IgPSBOQSxmb250c2l6ZSA9IDgsY2x1c3Rlcl9jb2xzID0gRixjbHVzdGVyaW5nX2Rpc3RhbmNlX3Jvd3MgPSAiZXVjbGlkZWFuIikKcGhlYXRtYXAoKGIvcm93TWF4cyhiKSlbLHBsb3QuY29sdW1uLm9yZGVyXSxib3JkZXJfY29sb3IgPSBOQSxmb250c2l6ZSA9IDgsY2x1c3Rlcl9jb2xzID0gRixjbHVzdGVyaW5nX2Rpc3RhbmNlX3Jvd3MgPSAiZXVjbGlkZWFuIikKcGhlYXRtYXAoKGMvcm93TWF4cyhjKSlbLHBsb3QuY29sdW1uLm9yZGVyXSxib3JkZXJfY29sb3IgPSBOQSxmb250c2l6ZSA9IDgsY2x1c3Rlcl9jb2xzID0gRixjbHVzdGVyX3Jvd3MgPSBGLGdhcHNfY29sID0gbGVuZ3RoKGVuZG8uc2VlZC5jb2x1bW5zKSxnYXBzX3JvdyA9IG5yb3coYSksc2hvd19yb3duYW1lcyA9IEYsc2hvd19jb2xuYW1lcyA9IFQsIG1haW4gPSAiRGV2ZWxvcG1lbnRhbCBFeHByZXNzaW9uIG9mIG1hdFRFcyIpCm1hdFRFLmRldiA8LSBwaGVhdG1hcCgoYy9yb3dNYXhzKGMpKVsscGxvdC5jb2x1bW4ub3JkZXJdLGJvcmRlcl9jb2xvciA9IE5BLGZvbnRzaXplID0gOCxjbHVzdGVyX2NvbHMgPSBGLGNsdXN0ZXJfcm93cyA9IEYsZ2Fwc19jb2wgPSBsZW5ndGgoZW5kby5zZWVkLmNvbHVtbnMpLGdhcHNfcm93ID0gbnJvdyhhKSxzaG93X3Jvd25hbWVzID0gRixzaG93X2NvbG5hbWVzID0gVCwgbWFpbiA9ICJEZXZlbG9wbWVudGFsIEV4cHJlc3Npb24gb2YgbWF0VEVzIikKYGBgCgoKCgpTdGFja2VkIGJhciB3aXRoIGltcHJpbnRlZCBmZWF0dXJlcywgc3ludGVsb2csIGV4cHJlc3Npb24udHlwZQpgYGB7cn0Kb2YuaW50ZXJlc3QgPC0gc3Vic2V0KGIuYm90aC5jb250cmFzdHMsYi5ib3RoLmNvbnRyYXN0cyRpbXByaW50ICE9ICJuby5pbXByaW50IikKb2YuaW50ZXJlc3QyIDwtIGRhdGEuZnJhbWUob2YuaW50ZXJlc3QgJT4lIGdyb3VwX2J5KGltcHJpbnQsc3ludGVsb2csZXhwcmVzc2lvbi50eXBlKSAlPiUgZHBseXI6OnN1bW1hcmlzZShmZWF0dXJlcyA9IG4oKSkpCm9mLmludGVyZXN0MiR0eXBlIDwtIGZhY3RvcihpZmVsc2Uob2YuaW50ZXJlc3QyJGltcHJpbnQgPT0gIm1hdFRFIiwgIm1hdFRFIixwYXN0ZShvZi5pbnRlcmVzdDIkaW1wcmludCxvZi5pbnRlcmVzdDIkc3ludGVsb2csc2VwPSJfIikpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzPWMoIlBFR19jb25zZXJ2ZWQubWFpemUiLCJQRUdfdmFyaWFibGUiLCJNRUdfY29uc2VydmVkLm1haXplIiwiTUVHX3ZhcmlhYmxlIiwibWF0VEUiKSkKb2YuaW50ZXJlc3QyJGV4cHJlc3Npb24udHlwZSA8LSBmYWN0b3Iob2YuaW50ZXJlc3QyJGV4cHJlc3Npb24udHlwZSxsZXZlbHM9YygiZW5kby5wcmVmZXJyZWQiLCJjb25zdGl0dXRpdmUiKSkKCmdncGxvdCgpICsgZ2VvbV9iYXIoYWVzKHg9dHlwZSx5PWZlYXR1cmVzLGZpbGw9ZXhwcmVzc2lvbi50eXBlKSxkYXRhPW9mLmludGVyZXN0MixzdGF0PSJpZGVudGl0eSIscG9zaXRpb24gPSBwb3NpdGlvbl9maWxsKCkpICsgdGhlbWVfY2xhc3NpYygpICArICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZT05MCwgaGp1c3Q9MSkpICsgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWMoImRhcmtvcmFuZ2UiLCJob25leWRldzQiKSkgCmBgYAoKQmluZGluZyBzaXRlcwpGaW5kIGJpbmRpbmcgc2l0ZXMgZnJvbSBCYXRpc3RhIGV0IGFsLiBpbiBCNzMKQ29udmVydCBzaXRlcyB0byBtZW1lIGZpbGUKbW9kdWxlIGxvYWQgbWVtZS80LjEyLjAtcHkyLW9wZW5tcGkzLXFicm01em4KaXVwYWMybWVtZSBUVFdDQ0FUQVRXID4gYmF0aXN0YV9tb3RpZjFfbWVtZS50eHQKaXVwYWMybWVtZSBDQ0FXQUFBVEdHID4gYmF0aXN0YV9tb3RpZjJfbWVtZS50eHQKY2F0IGJhdGlzdGFfbW90aWYxX21lbWUudHh0IGJhdGlzdGFfbW90aWYyX21lbWUudHh0ID4gYmF0aXN0YV9tb3RpZnNfbWVtZS50eHQKbW9kdWxlIGxvYWQgYmVkdG9vbHMyLzIuMjcuMS1zMm10cHN1CmN1dCAtZiAxLDQsNSw5IEI3My5zdHJ1Y3R1cmFsVEV2Mi4yMDE4LjEyLjIwLmZpbHRlcmVkVEUuZGlzam9pbmVkLmdmZjMgfCBiZWR0b29scyBnZXRmYXN0YSAtbmFtZSAtZmkgWmVhX21heXMuQUdQdjQuZG5hLnRvcGxldmVsLmZhIC1iZWQgLSAtZm8gQjczdjQuZGlzam9pbmVkVEUuZmEKZmltbyBiYXRpc3RhX21vdGlmc19tZW1lLnR4dCBCNzN2NC5kaXNqb2luZWRURS5mYSA+IGZpbW8udHh0CmNwIGZpbW9fb3V0L2ZpbW8udHh0IGJhdGlzdGFfbW90aWZzX2ZpbW9fb3V0LnR4dAoKZ3JlcCBnZW5lIFplYV9tYXlzLkFHUHY0LjMzLmdmZjMgfCBjdXQgLWYgMSw0LDUsOSAtIHwgYmVkdG9vbHMgZ2V0ZmFzdGEgLW5hbWUgLWZpIFplYV9tYXlzLkFHUHY0LmRuYS50b3BsZXZlbC5mYSAtYmVkIC0gLWZvIEI3M3Y0LmdlbmUuZmEKZmltbyBiYXRpc3RhX21vdGlmc19tZW1lLnR4dCBCNzN2NC5nZW5lLmZhIApjcCBmaW1vX291dC9maW1vLnR4dCBiYXRpc3RhX21vdGlmc19maW1vX2dlbmVfb3V0LnR4dAoKYGBge3J9CmFsbC5zaXRlcyA8LSByZWFkLnRhYmxlKCJiYXRpc3RhX21vdGlmc19maW1vX291dC50eHQiLGhlYWRlcj1GLHN0cmluZ3NBc0ZhY3RvcnMgPSBGLHNlcD0iXHQiKQphbGwuc2l0ZXMkdGUgPC0gc3Vic3RyKGFsbC5zaXRlcyRWMyw0LDI0KQpzaXRlcy5wZXIudGUgPC0gZGF0YS5mcmFtZShhbGwuc2l0ZXMgJT4lIGdyb3VwX2J5KHRlKSAlPiUgZHBseXI6OnN1bW1hcmlzZShtb3RpZjEgPSBzdW0oVjEgPT0gIlRUV0NDQVRBVFciKSxtb3RpZjIgPSBzdW0oVjEgPT0gIkNDQVdBQUFUR0ciKSkpCnNpdGVzLnBlci50ZSRtb3RpZi50eXBlIDwtIGlmZWxzZShzaXRlcy5wZXIudGUkbW90aWYxID4gMCAmIHNpdGVzLnBlci50ZSRtb3RpZjIgPiAwLCAiYm90aCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2Uoc2l0ZXMucGVyLnRlJG1vdGlmMSA+IDAsICJtb3RpZjEiLCJtb3RpZjIiKSkKbmFtZXMoc2l0ZXMucGVyLnRlKVsxXSA8LSAiZmVhdHVyZSIKc2l0ZXMucGVyLnRlJGZlYXR1cmVfdHlwZSA8LSAiVEUiCmBgYAoKR2VuZSBiaW5kaW5nIHNpdGVzCmBgYHtyfQpnZW5lLnNpdGVzIDwtIHJlYWQudGFibGUoImJhdGlzdGFfbW90aWZzX2ZpbW9fZ2VuZV9vdXQudHh0IixoZWFkZXI9RixzdHJpbmdzQXNGYWN0b3JzID0gRixzZXA9Ilx0IikKZ2VuZS5zaXRlcyRnZW5lIDwtIHN1YnN0cihnZW5lLnNpdGVzJFYzLDQsMTcpCmdlbmUuc2l0ZXMyIDwtIHN1YnNldChnZW5lLnNpdGVzLCFncmVwbCgiR1JNWk0iLGdlbmUuc2l0ZXMkZ2VuZSkpCnNpdGVzLnBlci5nZW5lIDwtIGRhdGEuZnJhbWUoZ2VuZS5zaXRlczIgJT4lIGdyb3VwX2J5KGdlbmUpICU+JSBkcGx5cjo6c3VtbWFyaXNlKG1vdGlmMSA9IHN1bShWMSA9PSAiVFRXQ0NBVEFUVyIpLG1vdGlmMiA9IHN1bShWMSA9PSAiQ0NBV0FBQVRHRyIpKSkKc2l0ZXMucGVyLmdlbmUkbW90aWYudHlwZSA8LSBpZmVsc2Uoc2l0ZXMucGVyLmdlbmUkbW90aWYxID4gMCAmIHNpdGVzLnBlci5nZW5lJG1vdGlmMiA+IDAsICJib3RoIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShzaXRlcy5wZXIuZ2VuZSRtb3RpZjEgPiAwLCAibW90aWYxIiwibW90aWYyIikpCm5hbWVzKHNpdGVzLnBlci5nZW5lKVsxXSA8LSAiZmVhdHVyZSIKc2l0ZXMucGVyLmdlbmUkZmVhdHVyZV90eXBlIDwtICJnZW5lIgpgYGAKCmBgYHtyfQpzaXRlcy5wZXIuZmVhdHVyZSA8LSBkYXRhLmZyYW1lKHJiaW5kKHNpdGVzLnBlci5nZW5lLHNpdGVzLnBlci50ZSkpCiN3cml0ZS50YWJsZShzaXRlcy5wZXIuZmVhdHVyZSxmaWxlPSJQSEUxX2JpbmRpbmdfc2l0ZXNfZ2VuZXNURXMudHh0IixxdW90ZT1GLHNlcD0iXHQiLHJvdy5uYW1lcyA9IEYpCmBgYAoKYGBge3J9CnNpdGVzLnBlci5mZWF0dXJlIDwtIHJlYWQudGFibGUoIlBIRTFfYmluZGluZ19zaXRlc19nZW5lc1RFcy50eHQiLHNlcCA9ICJcdCIsc3RyaW5nc0FzRmFjdG9ycyA9IEYsaGVhZGVyPVQpCmBgYAoKCmBgYHtyfQpiLmJvdGguY29udHJhc3RzJG1vdGlmMSA8LSBiLmJvdGguY29udHJhc3RzJGZlYXR1cmUgJWluJSBzdWJzZXQoc2l0ZXMucGVyLmZlYXR1cmUsc2l0ZXMucGVyLmZlYXR1cmUkbW90aWYxID4gMClbLCJmZWF0dXJlIl0KYi5ib3RoLmNvbnRyYXN0cyRtb3RpZjIgPC0gYi5ib3RoLmNvbnRyYXN0cyRmZWF0dXJlICVpbiUgc3Vic2V0KHNpdGVzLnBlci5mZWF0dXJlLHNpdGVzLnBlci5mZWF0dXJlJG1vdGlmMiA+IDApWywiZmVhdHVyZSJdCmIuYm90aC5jb250cmFzdHMkbW90aWYudHlwZSA8LSBpZmVsc2UoYi5ib3RoLmNvbnRyYXN0cyRtb3RpZjEgJiBiLmJvdGguY29udHJhc3RzJG1vdGlmMiwgImJvdGgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShiLmJvdGguY29udHJhc3RzJG1vdGlmMSwgIm1vdGlmMSIsaWZlbHNlKGIuYm90aC5jb250cmFzdHMkbW90aWYyLCJtb3RpZjIiLCJuby5tb3RpZiIpKSkKdGFibGUoYi5ib3RoLmNvbnRyYXN0cyRtb3RpZi50eXBlKQpgYGAKCmBgYHtyfQpzaXRlcy5zdW1tYXJ5IDwtIGRhdGEuZnJhbWUoYi5ib3RoLmNvbnRyYXN0cyAlPiUgZ3JvdXBfYnkoaW1wcmludCxtb3RpZi50eXBlKSAlPiUgZHBseXI6OnN1bW1hcmlzZShuID0gbigpKSAlPiUgZHBseXI6Om11dGF0ZShmcmVxID0gbi9zdW0obikpKQoKZ2dwbG90KCkgKyBnZW9tX2JhcihhZXMoeD1pbXByaW50LHk9bixmaWxsPW1vdGlmLnR5cGUpLGRhdGE9c2l0ZXMuc3VtbWFyeSxzdGF0PSJpZGVudGl0eSIscG9zaXRpb24gPSBwb3NpdGlvbl9maWxsKCkpICsgdGhlbWVfY2xhc3NpYygpICArICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZT05MCwgaGp1c3Q9MSkpICsgIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAiWWxHbkJ1IikKZ2dwbG90KCkgKyBnZW9tX2JhcihhZXMoeD1pbXByaW50LHk9ZnJlcSxmaWxsPW1vdGlmLnR5cGUpLGRhdGE9c2l0ZXMuc3VtbWFyeSxzdGF0PSJpZGVudGl0eSIscG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSgpKSArIHRoZW1lX2NsYXNzaWMoKSAgKyAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGU9OTAsIGhqdXN0PTEpKSArICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlID0gIllsR25CdSIpCgpiLmJvdGguY29udHJhc3RzJGVpdGhlci5tb3RpZiA8LSBpZmVsc2UoYi5ib3RoLmNvbnRyYXN0cyRtb3RpZi50eXBlID09ICJuby5tb3RpZiIsIm5vLm1vdGlmIiwiaGFzLm1vdGlmIikKc2l0ZXMuc3VtbWFyeTIgPC0gZGF0YS5mcmFtZShiLmJvdGguY29udHJhc3RzICU+JSBncm91cF9ieShpbXByaW50LGVpdGhlci5tb3RpZikgJT4lIGRwbHlyOjpzdW1tYXJpc2UobiA9IG4oKSkgJT4lIGRwbHlyOjptdXRhdGUoZnJlcSA9IG4vc3VtKG4pKSkKc2l0ZXMuc3VtbWFyeTIKYGBgCgpMb29rIGF0IHBlciBURSBmYW0gCmBgYHtyfQp0ZS5ib3RoLmNvbnRyYXN0cyA8LSBzdWJzZXQoYi5ib3RoLmNvbnRyYXN0cyxiLmJvdGguY29udHJhc3RzJGltcHJpbnQgPT0gIm1hdFRFIikKdGUuYm90aC5jb250cmFzdHMkZmFtIDwtIHN1YnN0cih0ZS5ib3RoLmNvbnRyYXN0cyRmZWF0dXJlLDAsOCkKCnRtcCA8LSBkYXRhLmZyYW1lKHRhYmxlKHRlLmJvdGguY29udHJhc3RzJGZhbSkpCnRtcDIgPC0gc3Vic2V0KHRtcCx0bXAkRnJlcT4xKQoKc2l0ZS50ZS5zdW1tYXJ5IDwtIGRhdGEuZnJhbWUoc3Vic2V0KHRlLmJvdGguY29udHJhc3RzLHRlLmJvdGguY29udHJhc3RzJGZhbSAlaW4lIHRtcDIkVmFyMSkgJT4lIGdyb3VwX2J5KG1vdGlmLnR5cGUsZmFtKSAlPiUgZHBseXI6OnN1bW1hcmlzZShmZWF0dXJlcyA9IG4oKSkpCgpnZ3Bsb3QoKSArIGdlb21fYmFyKGFlcyh4PWZhbSx5PWZlYXR1cmVzLGZpbGw9bW90aWYudHlwZSksZGF0YT1zaXRlLnRlLnN1bW1hcnksc3RhdD0iaWRlbnRpdHkiLHBvc2l0aW9uID0gcG9zaXRpb25fZmlsbCgpKSArIHRoZW1lX2NsYXNzaWMoKSAgKyAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGU9OTAsIGhqdXN0PTEpKSAKYGBgCkRISDIKYGBge3J9CkRISDIuYm90aC5jb250cmFzdHMgPC0gc3Vic2V0KGIuYm90aC5jb250cmFzdHMsZ3JlcGwoIkRISDAwMDAyIixiLmJvdGguY29udHJhc3RzJGZlYXR1cmUpKQoKREhIMi5ub3QuZXhwcmVzc2VkIDwtIHN1YnNldChpbXAuYWxsLGdyZXBsKCJESEgwMDAwMlptMDAwMDFkIixpbXAuYWxsJElEKSAmICFpbXAuYWxsJElEICVpbiUgYi5ib3RoLmNvbnRyYXN0cyRmZWF0dXJlKQpESEgyLm5vdC5leHByZXNzZWQyIDwtIGRhdGEuZnJhbWUobWF0cml4KG5jb2w9bmNvbChESEgyLmJvdGguY29udHJhc3RzKSxucm93PW5yb3coREhIMi5ub3QuZXhwcmVzc2VkKSkpCmNvbG5hbWVzKERISDIubm90LmV4cHJlc3NlZDIpIDwtIGNvbG5hbWVzKERISDIuYm90aC5jb250cmFzdHMpCkRISDIubm90LmV4cHJlc3NlZDIkZmVhdHVyZSA8LSBESEgyLm5vdC5leHByZXNzZWQkSUQKREhIMi5ub3QuZXhwcmVzc2VkMiRpbXByaW50IDwtICJub3QuZGV0ZWN0ZWQiCgpESEgyLmJvdGguY29udHJhc3RzMiA8LSByYmluZChESEgyLmJvdGguY29udHJhc3RzLERISDIubm90LmV4cHJlc3NlZDIpCgpESEgyLmJvdGguc2l0ZXMgPC0gbWVyZ2UoREhIMi5ib3RoLmNvbnRyYXN0czIsc2l0ZXMucGVyLmZlYXR1cmUsYnk9ImZlYXR1cmUiLGFsbC54PVQpCm5hbWVzKERISDIuYm90aC5zaXRlcykgPC0gZ3N1YigiLnkiLCIuc2l0ZXMiLG5hbWVzKERISDIuYm90aC5zaXRlcyksZml4ZWQgPSBUKQoKREhIMi5ib3RoLnNpdGVzJG1vdGlmMS5zaXRlc1tpcy5uYShESEgyLmJvdGguc2l0ZXMkbW90aWYxLnNpdGVzKV0gPC0gMApESEgyLmJvdGguc2l0ZXMkbW90aWYyLnNpdGVzW2lzLm5hKERISDIuYm90aC5zaXRlcyRtb3RpZjIuc2l0ZXMpXSA8LSAwCkRISDIuYm90aC5zaXRlcyRtb3RpZi50eXBlIDwtIGlmZWxzZShESEgyLmJvdGguc2l0ZXMkbW90aWYxLnNpdGVzID4gMCAmIERISDIuYm90aC5zaXRlcyRtb3RpZjIuc2l0ZXMgPiAwLCAiYm90aCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKERISDIuYm90aC5zaXRlcyRtb3RpZjEuc2l0ZXMgPiAwLCAibW90aWYxIixpZmVsc2UoREhIMi5ib3RoLnNpdGVzJG1vdGlmMi5zaXRlcyA+IDAsIm1vdGlmMiIsIm5vLm1vdGlmIikpKQoKc2l0ZS5kaGguc3VtbWFyeSA8LSBkYXRhLmZyYW1lKERISDIuYm90aC5zaXRlcyAlPiUgZ3JvdXBfYnkobW90aWYudHlwZSxpbXByaW50KSAlPiUgZHBseXI6OnN1bW1hcmlzZShmZWF0dXJlcyA9IG4oKSkpCgpnZ3Bsb3QoKSArIGdlb21fYmFyKGFlcyh4PWltcHJpbnQseT1mZWF0dXJlcyxmaWxsPW1vdGlmLnR5cGUpLGRhdGE9c2l0ZS5kaGguc3VtbWFyeSxzdGF0PSJpZGVudGl0eSIscG9zaXRpb24gPSBwb3NpdGlvbl9maWxsKCkpICsgdGhlbWVfY2xhc3NpYygpICArICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZT05MCwgaGp1c3Q9MSkpICsgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJZbEduQnUiKQoKZ2dwbG90KERISDIuYm90aC5zaXRlcyxhZXMoeD1pbXByaW50LHk9bW90aWYxLnNpdGVzLGZpbGw9aW1wcmludCkpICsgZ2VvbV9ib3hwbG90KCkgKyB0aGVtZV9jbGFzc2ljKCkgKyB0aGVtZShwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCkpICsgc3RhdF9zdW1tYXJ5KGZ1bi5kYXRhID0gZ2l2ZS5uLCBnZW9tID0gInRleHQiKSAKZ2dwbG90KERISDIuYm90aC5zaXRlcyxhZXMoeD1pbXByaW50LHk9bW90aWYyLnNpdGVzLGZpbGw9aW1wcmludCkpICsgZ2VvbV9ib3hwbG90KCkgKyB0aGVtZV9jbGFzc2ljKCkgKyB0aGVtZShwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCkpICsgc3RhdF9zdW1tYXJ5KGZ1bi5kYXRhID0gZ2l2ZS5uLCBnZW9tID0gInRleHQiKSAKYGBgCgoKCgpgYGB7cn0KYi5ib3RoLmNvbnRyYXN0cyRpbXByaW50LnN5bnRlbG9nIDwtIHBhc3RlKGIuYm90aC5jb250cmFzdHMkaW1wcmludCxiLmJvdGguY29udHJhc3RzJHN5bnRlbG9nLHNlcD0iXyIpCmdlbmUua2V5LnN5bnRlbmljLmdyYXNzZXMgPC0gc3Vic2V0KGdlbmUua2V5LG5jaGFyKGdlbmUua2V5JHNvcmdodW1fb3J0aG9sb2cpID4gMCAmIG5jaGFyKGdlbmUua2V5JGZveHRhaWxfbWlsbGV0X29ydGhvbG9nKSA+IDAgJiBuY2hhcihnZW5lLmtleSRyaWNlX29ydGhvbG9nKSA+IDAgJiBuY2hhcihnZW5lLmtleSRicmFjaHlwb2RpdW1fb3J0aG9sb2cpID4gMCkKYi5ib3RoLmNvbnRyYXN0cyRzeW50ZW5pYy5ncmFzc2VzIDwtIGlmZWxzZShiLmJvdGguY29udHJhc3RzJGZlYXR1cmVfdHlwZSA9PSAiVEUiLCJOQS5URSIsaWZlbHNlKGIuYm90aC5jb250cmFzdHMkZmVhdHVyZSAlaW4lIGdlbmUua2V5LnN5bnRlbmljLmdyYXNzZXMkdjRfZ2VuZV9tb2RlbCwgInN5bnRlbmljLmdyYXNzZXMiLCJub25zeW50ZW5pYy5ncmFzc2VzIikpCnRhYmxlKGIuYm90aC5jb250cmFzdHNbLGMoImltcHJpbnQiLCJzeW50ZW5pYy5ncmFzc2VzIiwic3ludGVsb2ciKV0pCgpzdW1tYXJpemUuc3ludGVueTEgPC0gZGF0YS5mcmFtZShzdWJzZXQoYi5ib3RoLmNvbnRyYXN0cyxiLmJvdGguY29udHJhc3RzJGZlYXR1cmVfdHlwZSA9PSAiZ2VuZSIpICU+JSBncm91cF9ieShpbXByaW50LHN5bnRlbG9nKSAlPiUgZHBseXI6OnN1bW1hcml6ZShuPW4oKSkgJT4lIGRwbHlyOjptdXRhdGUoZnJlcT1uL3N1bShuKSkpCgphIDwtIGdncGxvdCgpICsgZ2VvbV9iYXIoYWVzKHg9aW1wcmludCx5PWZyZXEsZmlsbD1zeW50ZWxvZyksZGF0YT1zdWJzZXQoc3VtbWFyaXplLnN5bnRlbnkxLHN1bW1hcml6ZS5zeW50ZW55MSRzeW50ZWxvZyA9PSAiUEFWIiksc3RhdD0iaWRlbnRpdHkiLHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2UoKSkgKyB0aGVtZV9jbGFzc2ljKCkgKyBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJncmVlbiIpKSArICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZT05MCwgaGp1c3Q9MSkpICsgZ2d0aXRsZSgiTm9uc3ludGVuaWMgd2l0aGluIG1haXplIikrIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKyB5bGltKGMoMCwxKSkKCnN1bW1hcml6ZS5zeW50ZW55MiA8LSBkYXRhLmZyYW1lKHN1YnNldChiLmJvdGguY29udHJhc3RzLGIuYm90aC5jb250cmFzdHMkZmVhdHVyZV90eXBlID09ICJnZW5lIikgJT4lIGdyb3VwX2J5KGltcHJpbnQsc3ludGVuaWMuZ3Jhc3NlcykgJT4lIGRwbHlyOjpzdW1tYXJpemUobj1uKCkpICU+JSBkcGx5cjo6bXV0YXRlKGZyZXE9bi9zdW0obikpKQoKYiA8LSBnZ3Bsb3QoKSArIGdlb21fYmFyKGFlcyh4PWltcHJpbnQseT1mcmVxLGZpbGw9c3ludGVuaWMuZ3Jhc3NlcyksZGF0YT1zdWJzZXQoc3VtbWFyaXplLnN5bnRlbnkyLHN1bW1hcml6ZS5zeW50ZW55MiRzeW50ZW5pYy5ncmFzc2VzID09ICJub25zeW50ZW5pYy5ncmFzc2VzIiksc3RhdD0iaWRlbnRpdHkiLHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2UoKSkgKyB0aGVtZV9jbGFzc2ljKCkgKyBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJmb3Jlc3RncmVlbiIpKSArICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZT05MCwgaGp1c3Q9MSkpICArIGdndGl0bGUoIk5vbnN5bnRlbmljIHdpdGhpbiBncmFzc2VzIikrIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKyB5bGltKGMoMCwxKSkKCmdyaWQuYXJyYW5nZShhLGIsbnJvdz0xKQpgYGAKCkhvdyBtYW55IGltcHJpbnRlZCBnZW5lcyBoYXZlIEdPIHRlcm1zPwpGaWxlIGRvd25sb2FkZWQgZnJvbSBBR1JJR08gdXNpbmcgbGluazogaHR0cDovL3N5c3RlbXNiaW9sb2d5LmNhdS5lZHUuY24vYWdyaUdPdjIvZG93bmxvYWQvOTU4X3NsaW1HTyBvbiAyMCBGZWIgMjAyMApgYGB7cn0KZ2VuZS5nbyA8LSByZWFkLnRhYmxlKCJCNzN2NF9zbGltR08udHh0IixoZWFkZXI9RixzZXA9Ilx0IixzdHJpbmdzQXNGYWN0b3JzID0gRikKZ2VuZS5nbyA8LSBnZW5lLmdvWywzOjRdCm5hbWVzKGdlbmUuZ28pIDwtIGMoImdlbmUiLCJ0ZXJtIikKYGBgCgpgYGB7cn0KYi5ib3RoLmNvbnRyYXN0cyRoYXMuZ28udGVybSA8LSBmYWN0b3IoaWZlbHNlKGIuYm90aC5jb250cmFzdHMkZmVhdHVyZV90eXBlID09ICJURSIsICJOQS5URSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShiLmJvdGguY29udHJhc3RzJGZlYXR1cmUgJWluJSBnZW5lLmdvJGdlbmUsICJHTy50ZXJtKHMpIiwibm8uR08iKSksbGV2ZWxzPWMoIkdPLnRlcm0ocykiLCJuby5HTyIpKQpzdW1tYXJpemUuZ28gPC0gZGF0YS5mcmFtZShzdWJzZXQoYi5ib3RoLmNvbnRyYXN0cyxiLmJvdGguY29udHJhc3RzJGZlYXR1cmVfdHlwZSA9PSAiZ2VuZSIpICU+JSBncm91cF9ieShpbXByaW50LnN5bnRlbG9nLGhhcy5nby50ZXJtLCkgJT4lIGRwbHlyOjpzdW1tYXJpc2UobiA9IG4oKSkgJT4lIGRwbHlyOjptdXRhdGUoZnJlcSA9IG4vc3VtKG4pKSkKCmdncGxvdCgpICsgZ2VvbV9iYXIoYWVzKHg9aW1wcmludC5zeW50ZWxvZyx5PWZyZXEsZmlsbD1oYXMuZ28udGVybSksZGF0YT1zdWJzZXQoc3VtbWFyaXplLmdvLHN1bW1hcml6ZS5nbyRoYXMuZ28udGVybSA9PSAibm8uR08iKSxzdGF0PSJpZGVudGl0eSIscG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSgpKSArIHRoZW1lX2NsYXNzaWMoKSArIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoInB1cnBsZSIpKSArICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZT05MCwgaGp1c3Q9MSkpICsgZ2d0aXRsZSgiR2VuZXMgd2l0aG91dCBHTyB0ZXJtIikrIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgCmBgYAoKYGBge3J9CmIuYm90aC5jb250cmFzdHMuZ2VuZXMgPC0gc3Vic2V0KGIuYm90aC5jb250cmFzdHMsYi5ib3RoLmNvbnRyYXN0cyRmZWF0dXJlX3R5cGUgPT0gImdlbmUiKQpiLmJvdGguYWxsdXYgPC0gZGF0YS5mcmFtZShzdWJzZXQoYi5ib3RoLmNvbnRyYXN0cy5nZW5lcyxiLmJvdGguY29udHJhc3RzLmdlbmVzJGltcHJpbnQgIT0gIm5vLmltcHJpbnQiKSAlPiUgZ3JvdXBfYnkoaW1wcmludCxzeW50ZWxvZyxzeW50ZW5pYy5ncmFzc2VzLGhhcy5nby50ZXJtKSAlPiUgZHBseXI6OnN1bW1hcmlzZShuPW4oKSkpCmdncGxvdChkYXRhPWIuYm90aC5hbGx1diwKICAgICAgIGFlcyhheGlzNCA9IGltcHJpbnQsYXhpczM9c3ludGVsb2csYXhpczI9c3ludGVuaWMuZ3Jhc3NlcyxheGlzMT1oYXMuZ28udGVybSx5PW4pKSArIAogIHNjYWxlX3hfZGlzY3JldGUobGltaXRzID0gcmV2KGMoImltcHJpbnQiLCJzeW50ZWxvZyIsICJzeW50ZW5pYy5ncmFzc2VzIiwgImhhcy5nby50ZXJtIikpLCBleHBhbmQgPSBjKC4xLCAuMDUpKSArCiAgeGxhYigiZmVhdHVyZXMiKSArCiAgZ2VvbV9hbGx1dml1bShhZXMoZmlsbCA9IGltcHJpbnQpKSArCiAgZ2VvbV9zdHJhdHVtKCkgKyBnZW9tX3RleHQoc3RhdCA9ICJzdHJhdHVtIiwgaW5mZXIubGFiZWwgPSBUUlVFKSArIGNvb3JkX2ZsaXAoKSsKICB0aGVtZV9jbGFzc2ljKCkgKyBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9YygibWFnZW50YTMiLCJuYXZ5IikpIApgYGAKCmBgYHtyfQpiLmJvdGguYWxsdXYyIDwtIGRhdGEuZnJhbWUoc3Vic2V0KGIuYm90aC5jb250cmFzdHMuZ2VuZXMsYi5ib3RoLmNvbnRyYXN0cy5nZW5lcyRpbXByaW50ICE9ICJuby5pbXByaW50IikgJT4lIGdyb3VwX2J5KGltcHJpbnQsc3ludGVsb2csaGFzLmdvLnRlcm0pICU+JSBkcGx5cjo6c3VtbWFyaXNlKG49bigpKSkKZ2dwbG90KGRhdGE9Yi5ib3RoLmFsbHV2MiwKICAgICAgIGFlcyhheGlzMyA9IGltcHJpbnQsYXhpczI9c3ludGVsb2csYXhpczE9aGFzLmdvLnRlcm0seT1uKSkgKwogIHNjYWxlX3hfZGlzY3JldGUobGltaXRzID0gcmV2KGMoImltcHJpbnQiLCJzeW50ZWxvZyIsICJoYXMuZ28udGVybSIpKSwgZXhwYW5kID0gYyguMSwgLjA1KSkgKwogIHhsYWIoImZlYXR1cmVzIikgKwogIGdlb21fYWxsdXZpdW0oYWVzKGZpbGwgPSBpbXByaW50KSkgKwogIGdlb21fc3RyYXR1bSgpICsgZ2VvbV90ZXh0KHN0YXQgPSAic3RyYXR1bSIsIGluZmVyLmxhYmVsID0gVFJVRSkgKyBjb29yZF9mbGlwKCkrCiAgdGhlbWVfY2xhc3NpYygpICsgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWMoIm1hZ2VudGEzIiwibmF2eSIpKSAKCmBgYAoKYGBge3J9CmIuYm90aC5hbGx1djJiIDwtIGRhdGEuZnJhbWUoYi5ib3RoLmNvbnRyYXN0cy5nZW5lcyAlPiUgZ3JvdXBfYnkoaW1wcmludCxzeW50ZW5pYy5ncmFzc2VzLGhhcy5nby50ZXJtLGV4cHJlc3Npb24udHlwZSkgJT4lIGRwbHlyOjpzdW1tYXJpc2Uobj1uKCkpKQpiLmJvdGguYWxsdXYyYiRzeW50ZW5pYy5ncmFzc2VzIDwtIGZhY3RvcihiLmJvdGguYWxsdXYyYiRzeW50ZW5pYy5ncmFzc2VzLGxldmVscz1jKCJub25zeW50ZW5pYy5ncmFzc2VzIiwic3ludGVuaWMuZ3Jhc3NlcyIpKQpiLmJvdGguYWxsdXYyYiRoYXMuZ28udGVybSA8LSBmYWN0b3IoYi5ib3RoLmFsbHV2MmIkaGFzLmdvLnRlcm0sbGV2ZWxzPWMoIm5vLkdPIiwiR08udGVybShzKSIpKQpiLmJvdGguYWxsdXYyYiRleHByZXNzaW9uLnR5cGUgPC0gZmFjdG9yKGIuYm90aC5hbGx1djJiJGV4cHJlc3Npb24udHlwZSxsZXZlbHM9YygiZW5kby5wcmVmZXJyZWQiLCJjb25zdGl0dXRpdmUiKSkKCmdncGxvdChkYXRhPWIuYm90aC5hbGx1djJiLAogICAgICAgYWVzKGF4aXM0ID0gaW1wcmludCxheGlzMz1zeW50ZW5pYy5ncmFzc2VzLGF4aXMyPWhhcy5nby50ZXJtLGF4aXMxPWV4cHJlc3Npb24udHlwZSx5PW4pKSArCiAgc2NhbGVfeF9kaXNjcmV0ZShsaW1pdHMgPSByZXYoYygiaW1wcmludCIsInN5bnRlbmljLmdyYXNzZXMiLCAiaGFzLmdvLnRlcm0iLCJleHByZXNzaW9uLnR5cGUiKSksIGV4cGFuZCA9IGMoLjEsIC4wNSkpICsKICB4bGFiKCJmZWF0dXJlcyIpICsKICBnZW9tX2FsbHV2aXVtKGFlcyhmaWxsID0gc3ludGVuaWMuZ3Jhc3NlcykpICsKICBnZW9tX3N0cmF0dW0oKSArIGdlb21fdGV4dChzdGF0ID0gInN0cmF0dW0iLCBpbmZlci5sYWJlbCA9IFRSVUUpICsgY29vcmRfZmxpcCgpKwogIHRoZW1lX2NsYXNzaWMoKSArIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1jKCJncmVlbiIsInN0ZWVsYmx1ZSIpKSArIGZhY2V0X2dyaWQoLn5pbXByaW50LHNjYWxlcz0iZnJlZSIpCmBgYApTdGF0cyBmb3IgYWJvdmUgLSAgY2hpc3F1YXJlZCB0ZXN0CmBgYHtyfQptLnN0YXRzIDwtIHN1YnNldChiLmJvdGguYWxsdXYyYixiLmJvdGguYWxsdXYyYiRpbXByaW50ICVpbiUgYygiTUVHIiwibm8uaW1wcmludCIpKQphIDwtIGRhdGEuZnJhbWUobS5zdGF0cyAlPiUgZ3JvdXBfYnkoaW1wcmludCxzeW50ZW5pYy5ncmFzc2VzKSAlPiUgZHBseXI6OnN1bW1hcmlzZShuPXN1bShuKSkgJT4lIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSBzeW50ZW5pYy5ncmFzc2VzLCB2YWx1ZXNfZnJvbSA9IG4pKQphJHBlcmNlbnQgPC0gYSRzeW50ZW5pYy5ncmFzc2VzL3Jvd1N1bXMoYVssMjozXSkKY2hpc3EudGVzdChhWywyOjNdKQoKYiA8LSBkYXRhLmZyYW1lKG0uc3RhdHMgJT4lIGdyb3VwX2J5KGltcHJpbnQsaGFzLmdvLnRlcm0pICU+JSBkcGx5cjo6c3VtbWFyaXNlKG49c3VtKG4pKSAlPiUgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IGhhcy5nby50ZXJtLCB2YWx1ZXNfZnJvbSA9IG4pKQpiJHBlcmNlbnQgPC0gYiRuby5HTy9yb3dTdW1zKGJbLDI6M10pCmNoaXNxLnRlc3QoYlssMjozXSkKCmMgPC0gZGF0YS5mcmFtZShtLnN0YXRzICU+JSBncm91cF9ieShpbXByaW50LGV4cHJlc3Npb24udHlwZSkgJT4lIGRwbHlyOjpzdW1tYXJpc2Uobj1zdW0obikpICU+JSBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gZXhwcmVzc2lvbi50eXBlLCB2YWx1ZXNfZnJvbSA9IG4pKQpjJHBlcmNlbnQgPC0gYyRlbmRvLnByZWZlcnJlZC9yb3dTdW1zKGNbLDI6M10pCmNoaXNxLnRlc3QoY1ssMjozXSkKCnAuc3RhdHMgPC0gc3Vic2V0KGIuYm90aC5hbGx1djJiLGIuYm90aC5hbGx1djJiJGltcHJpbnQgJWluJSBjKCJQRUciLCJuby5pbXByaW50IikpCmQgPC0gZGF0YS5mcmFtZShwLnN0YXRzICU+JSBncm91cF9ieShpbXByaW50LHN5bnRlbmljLmdyYXNzZXMpICU+JSBkcGx5cjo6c3VtbWFyaXNlKG49c3VtKG4pKSAlPiUgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IHN5bnRlbmljLmdyYXNzZXMsIHZhbHVlc19mcm9tID0gbikpCmQkcGVyY2VudCA8LSBkJHN5bnRlbmljLmdyYXNzZXMvcm93U3VtcyhkWywyOjNdKQpjaGlzcS50ZXN0KGRbLDI6M10pCgplIDwtIGRhdGEuZnJhbWUocC5zdGF0cyAlPiUgZ3JvdXBfYnkoaW1wcmludCxoYXMuZ28udGVybSkgJT4lIGRwbHlyOjpzdW1tYXJpc2Uobj1zdW0obikpICU+JSBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gaGFzLmdvLnRlcm0sIHZhbHVlc19mcm9tID0gbikpCmUkcGVyY2VudCA8LSBlJG5vLkdPL3Jvd1N1bXMoZVssMjozXSkKY2hpc3EudGVzdChlWywyOjNdKQoKZiA8LSBkYXRhLmZyYW1lKHAuc3RhdHMgJT4lIGdyb3VwX2J5KGltcHJpbnQsZXhwcmVzc2lvbi50eXBlKSAlPiUgZHBseXI6OnN1bW1hcmlzZShuPXN1bShuKSkgJT4lIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSBleHByZXNzaW9uLnR5cGUsIHZhbHVlc19mcm9tID0gbikpCmYkcGVyY2VudCA8LSBmJGVuZG8ucHJlZmVycmVkL3Jvd1N1bXMoZlssMjozXSkKY2hpc3EudGVzdChmWywyOjNdKQpgYGAKVmVyc2lvbiAyCmBgYHtyfQpiLmJvdGguYWxsdXYyYiA8LSBkYXRhLmZyYW1lKGIuYm90aC5jb250cmFzdHMuZ2VuZXMgJT4lIGdyb3VwX2J5KGltcHJpbnQsc3ludGVsb2csc3ludGVuaWMuZ3Jhc3NlcyxleHByZXNzaW9uLnR5cGUpICU+JSBkcGx5cjo6c3VtbWFyaXNlKG49bigpKSkKYi5ib3RoLmFsbHV2MmIkc3ludGVuaWMuZ3Jhc3NlcyA8LSBmYWN0b3IoYi5ib3RoLmFsbHV2MmIkc3ludGVuaWMuZ3Jhc3NlcyxsZXZlbHM9Yygibm9uc3ludGVuaWMuZ3Jhc3NlcyIsInN5bnRlbmljLmdyYXNzZXMiKSkKYi5ib3RoLmFsbHV2MmIkc3ludGVsb2cgPC0gZmFjdG9yKGIuYm90aC5hbGx1djJiJHN5bnRlbG9nLGxldmVscz1jKCJ2YXJpYWJsZSIsImNvbnNlcnZlZC5tYWl6ZSIpKQpiLmJvdGguYWxsdXYyYiRleHByZXNzaW9uLnR5cGUgPC0gZmFjdG9yKGIuYm90aC5hbGx1djJiJGV4cHJlc3Npb24udHlwZSxsZXZlbHM9YygiZW5kby5wcmVmZXJyZWQiLCJjb25zdGl0dXRpdmUiKSkKCmdncGxvdChkYXRhPWIuYm90aC5hbGx1djJiLAogICAgICAgYWVzKGF4aXM0ID0gaW1wcmludCxheGlzMyA9IHN5bnRlbG9nLGF4aXMyPXN5bnRlbmljLmdyYXNzZXMsYXhpczE9ZXhwcmVzc2lvbi50eXBlLHk9bikpICsKICBzY2FsZV94X2Rpc2NyZXRlKGxpbWl0cyA9IHJldihjKCJpbXByaW50Iiwic3ludGVsb2ciLCJzeW50ZW5pYy5ncmFzc2VzIiwiZXhwcmVzc2lvbi50eXBlIikpLCBleHBhbmQgPSBjKC4xLCAuMDUpKSArCiAgeGxhYigiZmVhdHVyZXMiKSArCiAgZ2VvbV9hbGx1dml1bShhZXMoZmlsbCA9IHN5bnRlbG9nKSkgKwogIGdlb21fc3RyYXR1bSgpICsgZ2VvbV90ZXh0KHN0YXQgPSAic3RyYXR1bSIsIGluZmVyLmxhYmVsID0gVFJVRSkgKyBjb29yZF9mbGlwKCkrCiAgdGhlbWVfY2xhc3NpYygpICsgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWMoImdyZWVuIiwic3RlZWxibHVlIikpICsgZmFjZXRfZ3JpZCgufmltcHJpbnQsc2NhbGVzPSJmcmVlIikKYGBgCgpgYGB7cn0KbS5zdGF0cyA8LSBzdWJzZXQoYi5ib3RoLmFsbHV2MmIsYi5ib3RoLmFsbHV2MmIkaW1wcmludCAlaW4lIGMoIk1FRyIsIm5vLmltcHJpbnQiKSkKcC5zdGF0cyA8LSBzdWJzZXQoYi5ib3RoLmFsbHV2MmIsYi5ib3RoLmFsbHV2MmIkaW1wcmludCAlaW4lIGMoIlBFRyIsIm5vLmltcHJpbnQiKSkKCmEgPC0gZGF0YS5mcmFtZShtLnN0YXRzICU+JSBncm91cF9ieShpbXByaW50LHN5bnRlbG9nKSAlPiUgZHBseXI6OnN1bW1hcmlzZShuPXN1bShuKSkgJT4lIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSBzeW50ZWxvZywgdmFsdWVzX2Zyb20gPSBuKSkKY2hpc3EudGVzdChhWywyOjNdKQoKZCA8LSBkYXRhLmZyYW1lKHAuc3RhdHMgJT4lIGdyb3VwX2J5KGltcHJpbnQsc3ludGVsb2cpICU+JSBkcGx5cjo6c3VtbWFyaXNlKG49c3VtKG4pKSAlPiUgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IHN5bnRlbG9nLCB2YWx1ZXNfZnJvbSA9IG4pKQpjaGlzcS50ZXN0KGRbLDI6M10pCmBgYAoKCmBgYHtyfQpoZWFkKHN1YnNldChiLmJvdGguY29udHJhc3RzLmdlbmVzLGIuYm90aC5jb250cmFzdHMuZ2VuZXMkaW1wcmludCA9PSAiTUVHIiAmIGIuYm90aC5jb250cmFzdHMuZ2VuZXMkc3ludGVuaWMuZ3Jhc3NlcyA9PSAibm9uc3ludGVuaWMuZ3Jhc3NlcyIgJiBiLmJvdGguY29udHJhc3RzLmdlbmVzJGhhcy5nby50ZXJtID09ICJuby5HTyIpKQpgYGAKCgoKCgoKCgptYXRURSB0aGluZ3MuCmBgYHtyfQpiLmJvdGgudGUgPC0gc3Vic2V0KGIuYm90aC5jb250cmFzdHMsYi5ib3RoLmNvbnRyYXN0cyRmZWF0dXJlX3R5cGUgPT0gIlRFIikKCnNoYXJlZC5CV1AgPC0gc3Vic2V0KHRlLmFubm8uY29tcGFyZSwgdGUuYW5uby5jb21wYXJlJEI3MyA9PSAicHJlc2VudCIgJiBuY2hhcih0ZS5hbm5vLmNvbXBhcmUkQjczLmFubm90YXRpb24gPT0gMjEpICYgdGUuYW5uby5jb21wYXJlJFcyMiA9PSAicHJlc2VudCIgJiBuY2hhcih0ZS5hbm5vLmNvbXBhcmUkVzIyLmFubm90YXRpb24gPT0gMjEpJiB0ZS5hbm5vLmNvbXBhcmUkUEgyMDcgPT0gInByZXNlbnQiICYgbmNoYXIodGUuYW5uby5jb21wYXJlJFBIMjA3LmFubm90YXRpb24gPT0gMjEpKQoKYi5ib3RoLnRlJHNoYXJlZC5URS5CVyA8LSBpZmVsc2UoYi5ib3RoLnRlJGZlYXR1cmUgJWluJSBzaGFyZWQuQlckQjczLmFubm90YXRpb24sICJzaGFyZWQuaW4uQlciLCJ2YXJpYWJsZS5CVyIpCmIuYm90aC50ZSRzaGFyZWQuVEUuQlAgPC0gaWZlbHNlKGIuYm90aC50ZSRmZWF0dXJlICVpbiUgc2hhcmVkLmJwJEI3My5hbm5vdGF0aW9uLCAic2hhcmVkLmluLkJQIiwidmFyaWFibGUuQlAiKQpiLmJvdGgudGUkc2hhcmVkLlRFIDwtIGlmZWxzZShiLmJvdGgudGUkZmVhdHVyZSAlaW4lIHNoYXJlZC5CV1AkQjczLmFubm90YXRpb24sICJzaGFyZWQuaW4uYWxsIiwidmFyaWFibGUiKQoKbWF0LnRlcyA8LSBzdWJzZXQoYi5ib3RoLnRlLGIuYm90aC50ZSRpbXByaW50ID09ICJtYXRURSIpCmBgYAoKYGBge3J9Cm1hdC50ZXMkb3JkZXIgPC0gc3Vic3RyKG1hdC50ZXMkZmVhdHVyZSwwLDIpCnRhYmxlKG1hdC50ZXNbLGMoIm9yZGVyIiwic2hhcmVkLlRFIildKQpgYGAKCmBgYHtyfQptYXQudGVzJGZhbSA8LSBzdWJzdHIobWF0LnRlcyRmZWF0dXJlLDAsOCkKdG1wIDwtIGRhdGEuZnJhbWUodGFibGUobWF0LnRlcyRmYW0pKQpucm93KHRtcCkKc3Vic2V0KHRtcCx0bXAkRnJlcSA+IDEpCmBgYAoKYGBge3J9CmIuYm90aC50ZXMgPC0gc3Vic2V0KGIuYm90aC5jb250cmFzdHMsYi5ib3RoLmNvbnRyYXN0cyRmZWF0dXJlX3R5cGUgPT0gIlRFIikKYi5ib3RoLnRlcyRvcmRlciA8LSBmYWN0b3Ioc3Vic3RyKGIuYm90aC50ZXMkZmVhdHVyZSwwLDIpLGxldmVscz1jKCJESCIsIkRUIiwiUkwiLCJSSSIsIlJTIikpCmIuYm90aC50ZXMkc2hhcmVkLlRFIDwtIGlmZWxzZShiLmJvdGgudGVzJGZlYXR1cmUgJWluJSBzaGFyZWQuQldQJEI3My5hbm5vdGF0aW9uLCAic2hhcmVkLmluLmFsbCIsInZhcmlhYmxlIikKCm1hdC50ZXMuYWxsdXYgPC0gZGF0YS5mcmFtZShzdWJzZXQoYi5ib3RoLnRlcyxiLmJvdGgudGVzJG9yZGVyICVpbiUgYygiUkwiLCJEVCIsIkRIIikpICU+JSBncm91cF9ieShpbXByaW50LG9yZGVyLHNoYXJlZC5URSxleHByZXNzaW9uLnR5cGUpICU+JSBkcGx5cjo6c3VtbWFyaXNlKG4gPSBuKCkpKQoKZ2dwbG90KGRhdGE9bWF0LnRlcy5hbGx1diwKICAgICAgIGFlcyhheGlzNCA9IGltcHJpbnQsIGF4aXMzID0gb3JkZXIsYXhpczI9c2hhcmVkLlRFLGF4aXMxPWV4cHJlc3Npb24udHlwZSx5PW4pKSArCiAgc2NhbGVfeF9kaXNjcmV0ZShsaW1pdHMgPSByZXYoYygiaW1wcmludCIsIm9yZGVyIiwic2hhcmVkLlRFIiwgImV4cHJlc3Npb24udHlwZSIpKSwgZXhwYW5kID0gYyguMSwgLjA1KSkgKwogIHhsYWIoImZlYXR1cmVzIikgKwogIGdlb21fYWxsdXZpdW0oYWVzKGZpbGwgPSBvcmRlcikpICsKICBnZW9tX3N0cmF0dW0oKSArIGdlb21fdGV4dChzdGF0ID0gInN0cmF0dW0iLCBpbmZlci5sYWJlbCA9IFRSVUUpICsgY29vcmRfZmxpcCgpKwogIHRoZW1lX2NsYXNzaWMoKSArIGZhY2V0X2dyaWQoLn5pbXByaW50LHNjYWxlcz0iZnJlZSIpICsgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWMoInN0ZWVsYmx1ZSIsImdyZWVuIiwiZGFya29yY2hpZDMiKSkKYGBgCgpgYGB7cn0KbWF0LnRlcy5hbGx1diA8LSBkYXRhLmZyYW1lKGIuYm90aC50ZXMgJT4lIGdyb3VwX2J5KGltcHJpbnQsb3JkZXIsc2hhcmVkLlRFLGV4cHJlc3Npb24udHlwZSkgJT4lIGRwbHlyOjpzdW1tYXJpc2UobiA9IG4oKSkpCgpnZ3Bsb3QoZGF0YT1zdWJzZXQobWF0LnRlcy5hbGx1dixtYXQudGVzLmFsbHV2JGltcHJpbnQgPT0gIm1hdFRFIiksCiAgICAgICBhZXMoYXhpczMgPSBvcmRlcixheGlzMj1zaGFyZWQuVEUsYXhpczE9ZXhwcmVzc2lvbi50eXBlLHk9bikpICsKICBzY2FsZV94X2Rpc2NyZXRlKGxpbWl0cyA9IHJldihjKCJvcmRlciIsInNoYXJlZC5URSIsICJleHByZXNzaW9uLnR5cGUiKSksIGV4cGFuZCA9IGMoLjEsIC4wNSkpICsKICB4bGFiKCJmZWF0dXJlcyIpICsKICBnZW9tX2FsbHV2aXVtKGFlcyhmaWxsID0gb3JkZXIpKSArCiAgZ2VvbV9zdHJhdHVtKCkgKyBnZW9tX3RleHQoc3RhdCA9ICJzdHJhdHVtIiwgaW5mZXIubGFiZWwgPSBUUlVFKSArCiAgdGhlbWVfY2xhc3NpYygpICsgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWMoInN0ZWVsYmx1ZSIsImdyZWVuIiwiZGFya29yY2hpZDMiLCJob3RwaW5rIikpICsgY29vcmRfZmxpcCgpCmBgYAoKCgpgYGB7cn0KQjczLnRlLmF0dHJpYnV0ZXMgPC0gcmVhZC50YWJsZSgiQjczX2ZpbHRlcmVkVEVfY29tYmluZWRfYXR0cmlidXRlc18xMi4yMC4xOC50eHQiLGhlYWRlcj1ULHN0cmluZ3NBc0ZhY3RvcnMgPSBGLHNlcD0iXHQiKQpCNzMudGUuYXR0cmlidXRlcyRpbXByaW50IDwtIGlmZWxzZShCNzMudGUuYXR0cmlidXRlcyRURSAlaW4lIG1hdC50ZXMkZmVhdHVyZSwgIm1hdFRFIiwKICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoQjczLnRlLmF0dHJpYnV0ZXMkVEUgJWluJSBiLmJvdGguY29udHJhc3RzJGZlYXR1cmUsICJuby5pbXByaW50Iiwibm90LmRldGVjdGVkIikpCkxUUi5hZ2UgPC0gc3Vic2V0KEI3My50ZS5hdHRyaWJ1dGVzLCFpcy5uYShCNzMudGUuYXR0cmlidXRlcyRMVFJzaW1pbGFyaXR5KSkKZ2dwbG90KExUUi5hZ2UsYWVzKHg9aW1wcmludCx5PUxUUnNpbWlsYXJpdHksZmlsbD1pbXByaW50KSkrIGdlb21fYWJsaW5lKHNsb3BlPTAsIGludGVyY2VwdD0uMzMpICsgZ2VvbV9hYmxpbmUoc2xvcGU9MCwgaW50ZXJjZXB0PS42NikgKyBnZW9tX3Zpb2xpbigpICsgdGhlbWVfY2xhc3NpYygpICsgdGhlbWUocGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSxwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpKSAgICsgc3RhdF9zdW1tYXJ5KGZ1bi5kYXRhID0gZ2l2ZS5uLCBnZW9tID0gInRleHQiKSAKYGBgCgpgYGB7cn0KZ2dwbG90KHN1YnNldChCNzMudGUuYXR0cmlidXRlcyxCNzMudGUuYXR0cmlidXRlcyRvcmRlciAlaW4lIGMoIlJMIiwiRFQiLCJESCIpKSxhZXMoeD1pbXByaW50LHk9ZGlzam9pbmVkLGZpbGw9aW1wcmludCkpKyBnZW9tX2FibGluZShzbG9wZT0wLCBpbnRlcmNlcHQ9LjMzKSArIGdlb21fYWJsaW5lKHNsb3BlPTAsIGludGVyY2VwdD0uNjYpICsgZ2VvbV9ib3hwbG90KCkgKyB0aGVtZV9jbGFzc2ljKCkgKyB0aGVtZShwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCkpICAgKyBzdGF0X3N1bW1hcnkoZnVuLmRhdGEgPSBnaXZlLm4sIGdlb20gPSAidGV4dCIpICsgZmFjZXRfd3JhcCgufm9yZGVyLHNjYWxlcyA9ICJmcmVlIikKYGBgCgpUaXNzdWUtc3BlY2lmaWMgZXhwcmVzc2lvbgpgYGB7cn0KdGFibGUoYi5ib3RoLnRlWyxjKCJpbXByaW50IiwiZXhwcmVzc2lvbi50eXBlIildKQoxMzMvKDEzMyArIDEyKQoxNDY1LygxNDY1ICsgMTI0NykKYGBgCgoKYXJlIG1hdGVybmFsIFRFcyBlbnJpY2hlZCBmb3IgdGhlaXIgY2xvc2VzdCBnZW5lIGJlaW5nIGEgTUVHPwpgYGB7cn0KIyB3cml0ZS50YWJsZShtZWcudGVzWywxLGRyb3A9Rl0sZmlsZT0ibWF0VEVzLnR4dCIscXVvdGU9Rixyb3cubmFtZXMgPSBGLGNvbC5uYW1lcyA9IEYpCmBgYAoKIyBGaW5kIGNsb3Nlc3QgZ2VuZSB0byBtYXRURXMKbW9kdWxlIGxvYWQgYmVkdG9vbHMyLzIuMjcuMS1zMm10cHN1CnNvcnQgLWsgMSwxIC1rNCw0biBtYXRURXMuZ2ZmMyB8IGJlZHRvb2xzIGNsb3Nlc3QgLUQgYiAtdCBhbGwgLXdhIC13YiAgLWEgLSAtYiAvd29yay9MQVMvc25hLWxhYi9zbmEvQjc0djQvWmVhX21heXMuQUdQdjQuMzMuZ2VuZS5nZmYzID4gbWF0VEVfY2xvc2VzdF9nZW5lX3YyLmJlZApjdXQgLWYgOSwxOCwxOSBtYXRURV9jbG9zZXN0X2dlbmVfdjIuYmVkID4gbWF0VEVfY2xvc2VzdF9nZW5lX3YyLnR4dAoKYGBge3J9Cm1hdFRFX2Nsb3Nlc3RfZ2VuZSA8LSByZWFkLnRhYmxlKCJtYXRURV9jbG9zZXN0X2dlbmVfdjIudHh0IixoZWFkZXI9RixzdHJpbmdzQXNGYWN0b3JzID0gRixzZXA9Ilx0IikKbWF0VEVfY2xvc2VzdF9nZW5lJFRFIDwtIHN1YnN0cihtYXRURV9jbG9zZXN0X2dlbmUkVjEsNCwyNCkKbWF0VEVfY2xvc2VzdF9nZW5lJGdlbmUgPC0gc3Vic3RyKG1hdFRFX2Nsb3Nlc3RfZ2VuZSRWMiw0LDE3KQptYXRURV9jbG9zZXN0X2dlbmUkZGlzdGFuY2UgPC0gbWF0VEVfY2xvc2VzdF9nZW5lJFYzCm1hdFRFX2Nsb3Nlc3RfZ2VuZSRURS50eXBlIDwtIGlmZWxzZShtYXRURV9jbG9zZXN0X2dlbmUkVEUgJWluJSBtZWcudGVzJGZlYXR1cmUsIm1hdFRFIiwiTm8uSW1wcmludCIpCm1hdFRFX2Nsb3Nlc3RfZ2VuZSRnZW5lLnR5cGUgPC0gaWZlbHNlKG1hdFRFX2Nsb3Nlc3RfZ2VuZSRnZW5lICVpbiUgbWVnLmdlbmVzJGZlYXR1cmUsIk1FRyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShtYXRURV9jbG9zZXN0X2dlbmUkZ2VuZSAlaW4lIHBlZy5nZW5lcyRmZWF0dXJlLCAiUEVHIiwiTm8uSW1wcmludCIpKQptYXRURV9jbG9zZXN0X2dlbmUkZGlzdGFuY2UuY2F0IDwtIGlmZWxzZShtYXRURV9jbG9zZXN0X2dlbmUkZGlzdGFuY2UgPT0gMCwgIm92ZXJsYXAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoYWJzKG1hdFRFX2Nsb3Nlc3RfZ2VuZSRkaXN0YW5jZSkgPCA1MDAwLCAibmVhciIsImZhciIpKQp0YWJsZShtYXRURV9jbG9zZXN0X2dlbmVbLGMoImdlbmUudHlwZSIsImRpc3RhbmNlLmNhdCIpXSkKdGFibGUobWF0VEVfY2xvc2VzdF9nZW5lWyxjKCJnZW5lLnR5cGUiLCJURS50eXBlIildKQpgYGAKQmlub21pYWwgdGVzdCAtIGFyZSBtYXRURXMgZW5yaWNoZWQgZm9yIGhhdmluZyBNRUcgYXMgbmVhcmVzdCBnZW5lPwpgYGB7cn0Kc3VjY2Vzc2VzIDwtIG5yb3coc3Vic2V0KG1hdFRFX2Nsb3Nlc3RfZ2VuZSxtYXRURV9jbG9zZXN0X2dlbmUkZ2VuZS50eXBlID09ICJNRUciKSkKdHJpYWxzIDwtIGxlbmd0aCh1bmlxdWUobWF0VEVfY2xvc2VzdF9nZW5lJFRFKSkKcHJvcG9ydGlvbi5nZW5lcy5NRUcgPC0gbnJvdyhzdWJzZXQoYi5ib3RoLmNvbnRyYXN0cy5nZW5lcyxiLmJvdGguY29udHJhc3RzLmdlbmVzJGltcHJpbnQgPT0gIk1FRyIpKS9ucm93KGIuYm90aC5jb250cmFzdHMuZ2VuZXMpCmJpbm9tLnRlc3Qoc3VjY2Vzc2VzLHRyaWFscyxwcm9wb3J0aW9uLmdlbmVzLk1FRyxhbHRlcm5hdGl2ZSA9ICJncmVhdGVyIikKCnN1Y2Nlc3Nlcy5wZWcgPC0gMApwcm9wb3J0aW9uLmdlbmVzLlBFRyA8LSBucm93KHN1YnNldChiLmJvdGguY29udHJhc3RzLmdlbmVzLGIuYm90aC5jb250cmFzdHMuZ2VuZXMkaW1wcmludCA9PSAiUEVHIikpL25yb3coYi5ib3RoLmNvbnRyYXN0cy5nZW5lcykKYmlub20udGVzdChzdWNjZXNzZXMucGVnLHRyaWFscyxwcm9wb3J0aW9uLmdlbmVzLlBFRyxhbHRlcm5hdGl2ZSA9ICJsZXNzIikKCnBsb3QudGVzdCA8LSBkYXRhLmZyYW1lKG1hdHJpeChOQSxucm93PTQsbmNvbD0zKSkKbmFtZXMocGxvdC50ZXN0KSA8LSBjKCJzZXQiLCJpbXByaW50IiwiZnJlcSIpCnBsb3QudGVzdCRpbXByaW50IDwtIGMoIk1FRyIsIk1FRyIsIlBFRyIsIlBFRyIpCnBsb3QudGVzdCRzZXQgPC0gYygiTUVHLnN1Y2Nlc3MiLCJwcm9wb3J0aW9uLk1FRyIsIlBFRy5zdWNjZXNzIiwicHJvcG9ydGlvbi5QRUciKQpwbG90LnRlc3QkZnJlcSA8LSBjKHN1Y2Nlc3Nlcy90cmlhbHMscHJvcG9ydGlvbi5nZW5lcy5NRUcsMCxwcm9wb3J0aW9uLmdlbmVzLlBFRykKCmdncGxvdCgpICsgZ2VvbV9iYXIoYWVzKHg9c2V0LHk9ZnJlcSxmaWxsPSJyZWQiKSxkYXRhPXBsb3QudGVzdCxzdGF0PSJpZGVudGl0eSIscG9zaXRpb24gPSAiaWRlbnRpdHkiLCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArIHRoZW1lX21pbmltYWwoKSArIHlsaW0oMCwwLjE1KSAgKyAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGU9OTAsIGhqdXN0PTEpLHBhbmVsLmdyaWQubWFqb3IueCA9IGVsZW1lbnRfYmxhbmsoKSkgKyBmYWNldF93cmFwKGltcHJpbnR+LixzY2FsZXM9ImZyZWVfeCIpCmBgYApNYWtlIHZlcnNpb24gd2l0aCBOcyBhbmQgd2hlcmUgdXBzdHJlYW0gYW5kIGRvd25zdHJlYW0gYXJlIHNob3duCmBgYHtyfQptYXRURS5NRUcgPC0gc3Vic2V0KG1hdFRFX2Nsb3Nlc3RfZ2VuZSxtYXRURV9jbG9zZXN0X2dlbmUkZ2VuZS50eXBlID09ICJNRUciKVssYygiVEUiLCJnZW5lIiwiZGlzdGFuY2UiLCJURS50eXBlIiwiZ2VuZS50eXBlIildCgpwbG90LnRlc3QyIDwtIHBsb3QudGVzdFsyOjQsMToyXQpwbG90LnRlc3QyJGNvdW50IDwtIGMocHJvcG9ydGlvbi5nZW5lcy5NRUcgKiB0cmlhbHMsMCxwcm9wb3J0aW9uLmdlbmVzLlBFRyAqIHRyaWFscykKcGxvdC50ZXN0MiRncm91cCA8LSBjKCJleHBlY3RlZCIsIlBFRyIsImV4cGVjdGVkIikKcGxvdC50ZXN0Mls0LF0gPC0gYygiTUVHLnN1Y2Nlc3MiLCJNRUciLG5yb3coc3Vic2V0KG1hdFRFLk1FRyxtYXRURS5NRUckZGlzdGFuY2UgPCAwKSksInVwc3RyZWFtIikKcGxvdC50ZXN0Mls1LF0gPC0gYygiTUVHLnN1Y2Nlc3MiLCJNRUciLG5yb3coc3Vic2V0KG1hdFRFLk1FRyxtYXRURS5NRUckZGlzdGFuY2UgPT0gMCkpLCJvdmVybGFwIikKcGxvdC50ZXN0Mls2LF0gPC0gYygiTUVHLnN1Y2Nlc3MiLCJNRUciLG5yb3coc3Vic2V0KG1hdFRFLk1FRyxtYXRURS5NRUckZGlzdGFuY2UgPiAwKSksImRvd25zdHJlYW0iKQpwbG90LnRlc3QyJGNvdW50IDwtIGFzLm51bWVyaWMocGxvdC50ZXN0MiRjb3VudCkKcGxvdC50ZXN0MiRncm91cCA8LSBmYWN0b3IocGxvdC50ZXN0MiRncm91cCxsZXZlbHM9YygidXBzdHJlYW0iLCJvdmVybGFwIiwiZG93bnN0cmVhbSIsImV4cGVjdGVkIiwiUEVHIikpCnBsb3QudGVzdDIkc2V0IDwtIGZhY3RvcihjKCJNRUcuZXhwZWN0ZWQiLCJQRUcubmVhcmVzdCIsIlBFRy5leHBlY3RlZCIsIk1FRy5uZWFyZXN0IiwiTUVHLm5lYXJlc3QiLCJNRUcubmVhcmVzdCIpLGxldmVscz1jKCJNRUcubmVhcmVzdCIsIk1FRy5leHBlY3RlZCIsIlBFRy5uZWFyZXN0IiwiUEVHLmV4cGVjdGVkIikpCgpnZ3Bsb3QoKSArIGdlb21fYmFyKGFlcyh4PXNldCx5PWNvdW50LGZpbGw9Z3JvdXApLGNvbG9yPSJibGFjayIsZGF0YT1wbG90LnRlc3QyLHN0YXQ9ImlkZW50aXR5Iixwb3NpdGlvbiA9ICJpZGVudGl0eSIpICsgdGhlbWVfbWluaW1hbCgpICArICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZT05MCwgaGp1c3Q9MSkscGFuZWwuZ3JpZC5tYWpvci54ID0gZWxlbWVudF9ibGFuaygpKSArIGZhY2V0X3dyYXAoaW1wcmludH4uLHNjYWxlcz0iZnJlZV94IikgKyBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9YygiaW5kaWFucmVkMSIsImhvbmV5ZGV3IiwibGlnaHRibHVlMyIsImJsYWNrIiwieWVsbG93IikpICsgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcz1jKDAsMiw0LDYsOCwxMCksbGltaXRzID0gYygwLDExKSkgCmBgYAoKCgpUYWJsZSBvZiBtYXRURSBuZWFyZXN0IGdlbmUgaXMgTUVHCmBgYHtyfQptYXRURS5NRUckb3JkZXIgPC0gc3Vic3RyKG1hdFRFLk1FRyRURSwwLDMpCmZvcihpIGluIDE6bnJvdyhtYXRURS5NRUcpKXsKICBpZihtYXRURS5NRUdbaSwiZ2VuZSJdICVpbiUgcGxvdC5vdXQyLmJ3JGZlYXR1cmUpewogICAgbWF0VEUuTUVHW2ksInJhdGlvLkJXLmdlbmUiXSA8LSBzdWJzZXQocGxvdC5vdXQyLmJ3LHBsb3Qub3V0Mi5idyRmZWF0dXJlID09IG1hdFRFLk1FR1tpLCJnZW5lIl0pWywibWF0ZXJuYWxfcHJlZmVyZW5jZSJdCiAgfQogIGVsc2UobWF0VEUuTUVHW2ksInJhdGlvLkJXLmdlbmUiXSA8LSAiTkEiKQogIGlmKG1hdFRFLk1FR1tpLCJnZW5lIl0gJWluJSBwbG90Lm91dDIuYnAkZmVhdHVyZSl7CiAgICBtYXRURS5NRUdbaSwicmF0aW8uQlAuZ2VuZSJdIDwtIHN1YnNldChwbG90Lm91dDIuYnAscGxvdC5vdXQyLmJwJGZlYXR1cmUgPT0gbWF0VEUuTUVHW2ksImdlbmUiXSlbLCJtYXRlcm5hbF9wcmVmZXJlbmNlIl0KICB9CiAgZWxzZShtYXRURS5NRUdbaSwicmF0aW8uQlAuZ2VuZSJdIDwtICJOQSIpCiAgaWYobWF0VEUuTUVHW2ksIlRFIl0gJWluJSBwbG90Lm91dDIuYnckZmVhdHVyZSl7CiAgICBtYXRURS5NRUdbaSwicmF0aW8uQlcuVEUiXSA8LSBzdWJzZXQocGxvdC5vdXQyLmJ3LHBsb3Qub3V0Mi5idyRmZWF0dXJlID09IG1hdFRFLk1FR1tpLCJURSJdKVssIm1hdGVybmFsX3ByZWZlcmVuY2UiXQogIH0KICBlbHNlKG1hdFRFLk1FR1tpLCJyYXRpby5CVy5URSJdIDwtICJOQSIpCiAgaWYobWF0VEUuTUVHW2ksIlRFIl0gJWluJSBwbG90Lm91dDIuYnAkZmVhdHVyZSl7CiAgICBtYXRURS5NRUdbaSwicmF0aW8uQlAuVEUiXSA8LSBzdWJzZXQocGxvdC5vdXQyLmJwLHBsb3Qub3V0Mi5icCRmZWF0dXJlID09IG1hdFRFLk1FR1tpLCJURSJdKVssIm1hdGVybmFsX3ByZWZlcmVuY2UiXQogIH0KICBlbHNlKG1hdFRFLk1FR1tpLCJyYXRpby5CUC5URSJdIDwtICJOQSIpCiAgbWF0VEUuTUVHW2ksImdlbmUudmFyaWFiaWxpdHkiXSA8LSBzdWJzZXQoYi5ib3RoLmNvbnRyYXN0cyxiLmJvdGguY29udHJhc3RzJGZlYXR1cmUgPT0gbWF0VEUuTUVHW2ksImdlbmUiXSlbLCJzeW50ZWxvZyJdCiAgbWF0VEUuTUVHW2ksImdlbmUuZXhwcmVzc2lvbiJdIDwtIHN1YnNldChiLmJvdGguY29udHJhc3RzLGIuYm90aC5jb250cmFzdHMkZmVhdHVyZSA9PSBtYXRURS5NRUdbaSwiZ2VuZSJdKVssImV4cHJlc3Npb24udHlwZSJdCiAgbWF0VEUuTUVHW2ksIlRFLmV4cHJlc3Npb24iXSA8LSBzdWJzZXQoYi5ib3RoLmNvbnRyYXN0cyxiLmJvdGguY29udHJhc3RzJGZlYXR1cmUgPT0gbWF0VEUuTUVHW2ksIlRFIl0pWywiZXhwcmVzc2lvbi50eXBlIl0KfQojd3JpdGUudGFibGUobWF0VEUuTUVHW29yZGVyKG1hdFRFLk1FRyRkaXN0YW5jZSksXSxmaWxlPSJtYXRURV9jbG9zZXN0X2dlbmVfaXNfTUVHX3YyLnR4dCIscXVvdGU9RixzZXA9Ilx0Iixyb3cubmFtZXMgPSBGKQpgYGAKCmBgYHtyfQptYXRURS5NRUcyIDwtIG1lcmdlKG1hdFRFLk1FRyx0ZS5hbm5vLmNvbXBhcmUsYnk9IlRFIixhbGwueD1UKQpgYGAKCk91dHB1dCBmaWxlcwpgYGB7cn0KIyB3cml0ZS50YWJsZShiLmJvdGguY29udHJhc3RzLGZpbGU9IkI3M19mZWF0dXJlc19ib3RoX2NvbnRyYXN0cy5vdXQudHh0IixzZXA9Ilx0IixxdW90ZT1GLHJvdy5uYW1lcyA9IEYpCiMgbmFtZXMocGxvdC5vdXQyLmJ3KVsxNF0gPC0gIlJFUiIKIyBwbG90Lm91dDIuYnckaW1wcmludCA8LSBOVUxMCiMgd3JpdGUudGFibGUocGxvdC5vdXQyLmJ3LGZpbGU9IkI3My52LlcyMl9mZWF0dXJlcy5vdXQudHh0IixzZXA9Ilx0IixxdW90ZT1GLHJvdy5uYW1lcyA9IEYpCiMgbmFtZXMocGxvdC5vdXQyLmJwKVsxNF0gPC0gIlJFUiIKIyBwbG90Lm91dDIuYnAkaW1wcmludCA8LSBOVUxMCiMgd3JpdGUudGFibGUocGxvdC5vdXQyLmJwLGZpbGU9IkI3My52LlBIMjA3X2ZlYXR1cmVzLm91dC50eHQiLHNlcD0iXHQiLHF1b3RlPUYscm93Lm5hbWVzID0gRikKIyBuYW1lcyhwbG90Lm91dDIud3ApWzE0XSA8LSAiUkVSIgojIHBsb3Qub3V0Mi53cCRpbXByaW50IDwtIE5VTEwKIyB3cml0ZS50YWJsZShwbG90Lm91dDIud3AsZmlsZT0iVzIyLnYuUEgyMDdfZmVhdHVyZXMub3V0LnR4dCIsc2VwPSJcdCIscXVvdGU9Rixyb3cubmFtZXMgPSBGKQpgYGAKCgoKCgoKCgoKCgoK